1use crate::{
2 to_json_string, Coin, IbcCallbackRequest, IbcDstCallback, IbcMsg, IbcSrcCallback, IbcTimeout,
3};
4
5#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct EmptyMemo;
9#[derive(Clone, Debug, PartialEq, Eq)]
10pub struct WithMemo {
11 memo: String,
12}
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct WithSrcCallback {
15 src_callback: IbcSrcCallback,
16}
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct WithDstCallback {
19 dst_callback: IbcDstCallback,
20}
21#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct WithCallbacks {
23 src_callback: IbcSrcCallback,
24 dst_callback: IbcDstCallback,
25}
26
27pub trait MemoSource {
28 fn into_memo(self) -> Option<String>;
29}
30
31impl MemoSource for EmptyMemo {
32 fn into_memo(self) -> Option<String> {
33 None
34 }
35}
36
37impl MemoSource for WithMemo {
38 fn into_memo(self) -> Option<String> {
39 Some(self.memo)
40 }
41}
42
43impl MemoSource for WithSrcCallback {
44 fn into_memo(self) -> Option<String> {
45 Some(to_json_string(&IbcCallbackRequest::source(self.src_callback)).unwrap())
46 }
47}
48
49impl MemoSource for WithDstCallback {
50 fn into_memo(self) -> Option<String> {
51 Some(to_json_string(&IbcCallbackRequest::destination(self.dst_callback)).unwrap())
52 }
53}
54
55impl MemoSource for WithCallbacks {
56 fn into_memo(self) -> Option<String> {
57 Some(
58 to_json_string(&IbcCallbackRequest::both(
59 self.src_callback,
60 self.dst_callback,
61 ))
62 .unwrap(),
63 )
64 }
65}
66
67impl<M: MemoSource> TransferMsgBuilder<M> {
68 pub fn build(self) -> IbcMsg {
69 IbcMsg::Transfer {
70 channel_id: self.channel_id,
71 to_address: self.to_address,
72 amount: self.amount,
73 timeout: self.timeout,
74 memo: self.memo.into_memo(),
75 }
76 }
77}
78
79#[derive(Clone, Debug, PartialEq, Eq)]
80pub struct TransferMsgBuilder<MemoData> {
81 channel_id: String,
82 to_address: String,
83 amount: Coin,
84 timeout: IbcTimeout,
85 memo: MemoData,
86}
87
88impl TransferMsgBuilder<EmptyMemo> {
89 pub fn new(
91 channel_id: impl Into<String>,
92 to_address: impl Into<String>,
93 amount: Coin,
94 timeout: impl Into<IbcTimeout>,
95 ) -> Self {
96 Self {
97 channel_id: channel_id.into(),
98 to_address: to_address.into(),
99 amount,
100 timeout: timeout.into(),
101 memo: EmptyMemo,
102 }
103 }
104
105 pub fn with_memo(self, memo: impl Into<String>) -> TransferMsgBuilder<WithMemo> {
107 TransferMsgBuilder {
108 channel_id: self.channel_id,
109 to_address: self.to_address,
110 amount: self.amount,
111 timeout: self.timeout,
112 memo: WithMemo { memo: memo.into() },
113 }
114 }
115
116 pub fn with_src_callback(
121 self,
122 src_callback: IbcSrcCallback,
123 ) -> TransferMsgBuilder<WithSrcCallback> {
124 TransferMsgBuilder {
125 channel_id: self.channel_id,
126 to_address: self.to_address,
127 amount: self.amount,
128 timeout: self.timeout,
129 memo: WithSrcCallback { src_callback },
130 }
131 }
132
133 pub fn with_dst_callback(
138 self,
139 dst_callback: IbcDstCallback,
140 ) -> TransferMsgBuilder<WithDstCallback> {
141 TransferMsgBuilder {
142 channel_id: self.channel_id,
143 to_address: self.to_address,
144 amount: self.amount,
145 timeout: self.timeout,
146 memo: WithDstCallback { dst_callback },
147 }
148 }
149}
150
151impl TransferMsgBuilder<WithSrcCallback> {
152 pub fn with_dst_callback(
157 self,
158 dst_callback: IbcDstCallback,
159 ) -> TransferMsgBuilder<WithCallbacks> {
160 TransferMsgBuilder {
161 channel_id: self.channel_id,
162 to_address: self.to_address,
163 amount: self.amount,
164 timeout: self.timeout,
165 memo: WithCallbacks {
166 src_callback: self.memo.src_callback,
167 dst_callback,
168 },
169 }
170 }
171}
172
173impl TransferMsgBuilder<WithDstCallback> {
174 pub fn with_src_callback(
179 self,
180 src_callback: IbcSrcCallback,
181 ) -> TransferMsgBuilder<WithCallbacks> {
182 TransferMsgBuilder {
183 channel_id: self.channel_id,
184 to_address: self.to_address,
185 amount: self.amount,
186 timeout: self.timeout,
187 memo: WithCallbacks {
188 src_callback,
189 dst_callback: self.memo.dst_callback,
190 },
191 }
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use crate::{coin, Addr, Timestamp, Uint64};
198
199 use super::*;
200
201 #[test]
202 fn test_transfer_msg_builder() {
203 let src_callback = IbcSrcCallback {
204 address: Addr::unchecked("src"),
205 gas_limit: Some(Uint64::new(12345)),
206 };
207 let dst_callback = IbcDstCallback {
208 address: "dst".to_string(),
209 gas_limit: None,
210 };
211
212 let empty_memo_builder = TransferMsgBuilder::new(
213 "channel-0",
214 "cosmos1example",
215 coin(10, "ucoin"),
216 Timestamp::from_seconds(12345),
217 );
218
219 let empty = empty_memo_builder.clone().build();
220 let with_memo = empty_memo_builder.clone().with_memo("memo").build();
221
222 let with_src_callback_builder = empty_memo_builder
223 .clone()
224 .with_src_callback(src_callback.clone());
225 let with_src_callback = with_src_callback_builder.clone().build();
226 let with_dst_callback_builder = empty_memo_builder
227 .clone()
228 .with_dst_callback(dst_callback.clone());
229 let with_dst_callback = with_dst_callback_builder.clone().build();
230
231 let with_both_callbacks1 = with_src_callback_builder
232 .with_dst_callback(dst_callback.clone())
233 .build();
234
235 let with_both_callbacks2 = with_dst_callback_builder
236 .with_src_callback(src_callback.clone())
237 .build();
238
239 assert_eq!(
241 empty,
242 IbcMsg::Transfer {
243 channel_id: "channel-0".to_string(),
244 to_address: "cosmos1example".to_string(),
245 amount: coin(10, "ucoin"),
246 timeout: Timestamp::from_seconds(12345).into(),
247 memo: None,
248 }
249 );
250 assert_eq!(
251 with_memo,
252 IbcMsg::Transfer {
253 channel_id: "channel-0".to_string(),
254 to_address: "cosmos1example".to_string(),
255 amount: coin(10, "ucoin"),
256 timeout: Timestamp::from_seconds(12345).into(),
257 memo: Some("memo".to_string()),
258 }
259 );
260 assert_eq!(
261 with_src_callback,
262 IbcMsg::Transfer {
263 channel_id: "channel-0".to_string(),
264 to_address: "cosmos1example".to_string(),
265 amount: coin(10, "ucoin"),
266 timeout: Timestamp::from_seconds(12345).into(),
267 memo: Some(
268 to_json_string(&IbcCallbackRequest::source(src_callback.clone())).unwrap()
269 ),
270 }
271 );
272 assert_eq!(
273 with_dst_callback,
274 IbcMsg::Transfer {
275 channel_id: "channel-0".to_string(),
276 to_address: "cosmos1example".to_string(),
277 amount: coin(10, "ucoin"),
278 timeout: Timestamp::from_seconds(12345).into(),
279 memo: Some(
280 to_json_string(&IbcCallbackRequest::destination(dst_callback.clone())).unwrap()
281 ),
282 }
283 );
284 assert_eq!(
285 with_both_callbacks1,
286 IbcMsg::Transfer {
287 channel_id: "channel-0".to_string(),
288 to_address: "cosmos1example".to_string(),
289 amount: coin(10, "ucoin"),
290 timeout: Timestamp::from_seconds(12345).into(),
291 memo: Some(
292 to_json_string(&IbcCallbackRequest::both(src_callback, dst_callback)).unwrap()
293 ),
294 }
295 );
296 assert_eq!(with_both_callbacks1, with_both_callbacks2);
297 }
298}