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}