Skip to main content

thruput_contract_util/
remote.rs

1//! Invoking a deployed contract from off-chain code.
2//!
3//! With the `thruput-sdk/remote` feature, every `#[contract]` interface also
4//! generates a remote proxy (`<Iface>Remote<I>`) next to its guest client.
5//! The proxy has one method per interface method. Each method encodes its
6//! arguments the way the guest client does and passes them, with the method's
7//! [`RemoteMethod`] identity, to a caller-supplied [`RemoteInvoker`]. The
8//! invoker decides what a prepared invocation is: a transaction to sign and
9//! submit, a simulation, or a test harness call.
10
11use thruput_xdr::contract::ContractEntrypointID;
12use thruput_xdr::types::ContractAddress;
13use xdrs::{ReadXDROwned, WriteXDR};
14
15/// A method's declared effect, from `#[contract(effect = "...")]`.
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum MethodEffect {
18    /// Uses only arguments and constants.
19    Pure,
20    /// Reads state or execution context, without application effects.
21    ReadOnly,
22    /// May change storage, emit events, transfer, or deploy.
23    StateChanging,
24}
25
26/// The identity of one interface method, fixed when its interface compiles.
27#[derive(Debug, Clone, Copy, PartialEq, Eq)]
28pub struct RemoteMethod {
29    /// Interface name, or `None` for an unnamed interface.
30    pub interface: Option<&'static str>,
31    /// Interface version the entrypoint was derived from.
32    pub version: &'static str,
33    /// Method name.
34    pub name: &'static str,
35    /// Entrypoint selector.
36    pub entrypoint: ContractEntrypointID,
37    /// Declared effect, or `None` when the interface declares none.
38    pub effect: Option<MethodEffect>,
39}
40
41impl RemoteMethod {
42    /// Whether the declaration permits simulating instead of submitting.
43    /// An undeclared effect is never treated as read-only.
44    pub fn is_read_only(&self) -> bool {
45        matches!(
46            self.effect,
47            Some(MethodEffect::Pure | MethodEffect::ReadOnly)
48        )
49    }
50}
51
52/// Prepares invocations of deployed contracts for a remote proxy.
53///
54/// The proxy routes `pure` and `readOnly` methods to [`view`](Self::view)
55/// and every other method, including those without a declared effect, to
56/// [`call`](Self::call). `args` holds the method's arguments: the single
57/// argument itself, or a tuple of several. Implementations encode it before
58/// returning, since the prepared invocation cannot borrow it.
59pub trait RemoteInvoker {
60    /// A prepared invocation of a method that may change state.
61    type Call<R>;
62    /// A prepared read of a `pure` or `readOnly` method.
63    type View<R>;
64
65    /// Prepare an invocation of `method` on `target`.
66    fn call<R, A>(
67        &self,
68        target: &ContractAddress,
69        method: RemoteMethod,
70        args: &A,
71    ) -> Self::Call<R>
72    where
73        R: ReadXDROwned,
74        A: WriteXDR + ?Sized;
75
76    /// Prepare a read of `method` on `target`.
77    fn view<R, A>(
78        &self,
79        target: &ContractAddress,
80        method: RemoteMethod,
81        args: &A,
82    ) -> Self::View<R>
83    where
84        R: ReadXDROwned,
85        A: WriteXDR + ?Sized;
86}