sails_rs/gstd/
calls.rs

1use super::message_future::MessageFutureExtended;
2use crate::{
3    calls::{Action, Remoting},
4    errors::Result,
5    futures::FutureExt,
6    prelude::*,
7};
8use core::future::Future;
9use gstd::{msg, prog};
10
11#[derive(Default)]
12pub struct GStdArgs {
13    wait_up_to: Option<BlockCount>,
14    #[cfg(not(feature = "ethexe"))]
15    reply_deposit: Option<GasUnit>,
16    #[cfg(not(feature = "ethexe"))]
17    reply_hook: Option<Box<dyn FnOnce() + Send + 'static>>,
18    redirect_on_exit: bool,
19}
20
21impl GStdArgs {
22    pub fn with_wait_up_to(self, wait_up_to: Option<BlockCount>) -> Self {
23        Self { wait_up_to, ..self }
24    }
25
26    pub fn with_redirect_on_exit(self, redirect_on_exit: bool) -> Self {
27        Self {
28            redirect_on_exit,
29            ..self
30        }
31    }
32
33    pub fn wait_up_to(&self) -> Option<BlockCount> {
34        self.wait_up_to
35    }
36
37    pub fn redirect_on_exit(&self) -> bool {
38        self.redirect_on_exit
39    }
40}
41
42#[cfg(not(feature = "ethexe"))]
43impl GStdArgs {
44    pub fn with_reply_deposit(self, reply_deposit: Option<GasUnit>) -> Self {
45        Self {
46            reply_deposit,
47            ..self
48        }
49    }
50
51    pub fn with_reply_hook<F: FnOnce() + Send + 'static>(self, f: F) -> Self {
52        Self {
53            reply_hook: Some(Box::new(f)),
54            ..self
55        }
56    }
57
58    pub fn reply_deposit(&self) -> Option<GasUnit> {
59        self.reply_deposit
60    }
61}
62
63#[derive(Debug, Default, Clone)]
64pub struct GStdRemoting;
65
66impl GStdRemoting {
67    pub fn new() -> Self {
68        Self
69    }
70}
71
72impl Remoting for GStdRemoting {
73    type Args = GStdArgs;
74
75    async fn activate(
76        self,
77        code_id: CodeId,
78        salt: impl AsRef<[u8]>,
79        payload: impl AsRef<[u8]>,
80        #[cfg(not(feature = "ethexe"))] gas_limit: Option<GasUnit>,
81        value: ValueUnit,
82        #[allow(unused_variables)] args: GStdArgs,
83    ) -> Result<impl Future<Output = Result<(ActorId, Vec<u8>)>>> {
84        #[cfg(not(feature = "ethexe"))]
85        let mut reply_future = if let Some(gas_limit) = gas_limit {
86            prog::create_program_bytes_with_gas_for_reply(
87                code_id,
88                salt,
89                payload,
90                gas_limit,
91                value,
92                args.reply_deposit.unwrap_or_default(),
93            )?
94        } else {
95            prog::create_program_bytes_for_reply(
96                code_id,
97                salt,
98                payload,
99                value,
100                args.reply_deposit.unwrap_or_default(),
101            )?
102        };
103        #[cfg(feature = "ethexe")]
104        let mut reply_future = prog::create_program_bytes_for_reply(code_id, salt, payload, value)?;
105
106        reply_future = reply_future.up_to(args.wait_up_to)?;
107
108        #[cfg(not(feature = "ethexe"))]
109        if let Some(reply_hook) = args.reply_hook {
110            reply_future = reply_future.handle_reply(reply_hook)?;
111        }
112
113        let reply_future = reply_future.map(|result| result.map_err(Into::into));
114        Ok(reply_future)
115    }
116
117    async fn message(
118        self,
119        target: ActorId,
120        payload: impl AsRef<[u8]>,
121        #[cfg(not(feature = "ethexe"))] gas_limit: Option<GasUnit>,
122        value: ValueUnit,
123        args: GStdArgs,
124    ) -> Result<impl Future<Output = Result<Vec<u8>>>> {
125        let reply_future = send_for_reply(
126            target,
127            payload,
128            #[cfg(not(feature = "ethexe"))]
129            gas_limit,
130            value,
131            args,
132        )?;
133        Ok(reply_future)
134    }
135
136    async fn query(
137        self,
138        target: ActorId,
139        payload: impl AsRef<[u8]>,
140        #[cfg(not(feature = "ethexe"))] gas_limit: Option<GasUnit>,
141        value: ValueUnit,
142        args: GStdArgs,
143    ) -> Result<Vec<u8>> {
144        let reply_future = send_for_reply(
145            target,
146            payload,
147            #[cfg(not(feature = "ethexe"))]
148            gas_limit,
149            value,
150            args,
151        )?;
152        let reply = reply_future.await?;
153        Ok(reply)
154    }
155}
156
157#[cfg(not(feature = "ethexe"))]
158pub(crate) fn send_for_reply_future(
159    target: ActorId,
160    payload: &[u8],
161    gas_limit: Option<GasUnit>,
162    value: ValueUnit,
163    args: GStdArgs,
164) -> Result<msg::MessageFuture> {
165    // here can be a redirect target
166    let mut message_future = if let Some(gas_limit) = gas_limit {
167        msg::send_bytes_with_gas_for_reply(
168            target,
169            payload,
170            gas_limit,
171            value,
172            args.reply_deposit.unwrap_or_default(),
173        )?
174    } else {
175        msg::send_bytes_for_reply(
176            target,
177            payload,
178            value,
179            args.reply_deposit.unwrap_or_default(),
180        )?
181    };
182
183    message_future = message_future.up_to(args.wait_up_to)?;
184
185    if let Some(reply_hook) = args.reply_hook {
186        message_future = message_future.handle_reply(reply_hook)?;
187    }
188    Ok(message_future)
189}
190
191#[cfg(feature = "ethexe")]
192pub(crate) fn send_for_reply_future(
193    target: ActorId,
194    payload: &[u8],
195    value: ValueUnit,
196    args: GStdArgs,
197) -> Result<msg::MessageFuture> {
198    // here can be a redirect target
199    let mut message_future = msg::send_bytes_for_reply(target, payload, value)?;
200
201    message_future = message_future.up_to(args.wait_up_to)?;
202
203    Ok(message_future)
204}
205
206pub(crate) fn send_for_reply<T: AsRef<[u8]>>(
207    target: ActorId,
208    payload: T,
209    #[cfg(not(feature = "ethexe"))] gas_limit: Option<GasUnit>,
210    value: ValueUnit,
211    args: GStdArgs,
212) -> Result<MessageFutureExtended<T>> {
213    #[cfg(not(feature = "ethexe"))]
214    let reply_deposit = args.reply_deposit;
215    let wait_up_to = args.wait_up_to;
216    let redirect_on_exit = args.redirect_on_exit;
217    let message_future = send_for_reply_future(
218        target,
219        payload.as_ref(),
220        #[cfg(not(feature = "ethexe"))]
221        gas_limit,
222        value,
223        args,
224    )?;
225
226    if redirect_on_exit {
227        Ok(MessageFutureExtended::with_redirect(
228            message_future,
229            target,
230            payload,
231            #[cfg(not(feature = "ethexe"))]
232            gas_limit,
233            value,
234            #[cfg(not(feature = "ethexe"))]
235            reply_deposit,
236            wait_up_to,
237        ))
238    } else {
239        Ok(MessageFutureExtended::without_redirect(message_future))
240    }
241}
242
243pub trait WithArgs {
244    fn with_wait_up_to(self, wait_up_to: Option<BlockCount>) -> Self;
245
246    #[cfg(not(feature = "ethexe"))]
247    fn with_reply_deposit(self, reply_deposit: Option<GasUnit>) -> Self;
248
249    #[cfg(not(feature = "ethexe"))]
250    fn with_reply_hook<F: FnOnce() + Send + 'static>(self, f: F) -> Self;
251
252    fn with_redirect_on_exit(self, redirect_on_exit: bool) -> Self;
253}
254
255impl<T> WithArgs for T
256where
257    T: Action<Args = GStdArgs>,
258{
259    fn with_wait_up_to(self, wait_up_to: Option<BlockCount>) -> Self {
260        self.with_args(|args| args.with_wait_up_to(wait_up_to))
261    }
262
263    #[cfg(not(feature = "ethexe"))]
264    fn with_reply_deposit(self, reply_deposit: Option<GasUnit>) -> Self {
265        self.with_args(|args| args.with_reply_deposit(reply_deposit))
266    }
267
268    #[cfg(not(feature = "ethexe"))]
269    fn with_reply_hook<F: FnOnce() + Send + 'static>(self, f: F) -> Self {
270        self.with_args(|args| args.with_reply_hook(f))
271    }
272
273    /// Set `redirect_on_exit` flag to `true``
274    ///
275    /// This flag is used to redirect a message to a new program when the target program exits
276    /// with an inheritor.
277    ///
278    /// WARNING: When this flag is set, the message future captures the payload and other arguments,
279    /// potentially resulting in multiple messages being sent. This can lead to increased gas consumption.
280    ///
281    /// This flag is set to `false`` by default.
282    fn with_redirect_on_exit(self, redirect_on_exit: bool) -> Self {
283        self.with_args(|args| args.with_redirect_on_exit(redirect_on_exit))
284    }
285}