thruput_testkit/caller.rs
1//! A cloneable handle for submitting transactions from an agent's account.
2//!
3//! [`AgentContext`](crate::AgentContext) holds the parts of an agent that must
4//! stay exclusive (its mailbox and random stream). [`Caller`] is the part that
5//! need not: the agent's account and the network it submits to. Clone one out
6//! with [`AgentContext::caller`](crate::AgentContext::caller) to hold it in a
7//! typed contract client, store it in a struct, or move it into a spawned task.
8
9use thruput_json_rpc_types::responses::TransactionReceipt;
10use thruput_xdr::contract::ContractEntrypointID;
11use thruput_xdr::types::ContractAddress;
12
13use crate::account::Account;
14use crate::error::Result;
15use crate::network::{Network, SubmittedTransaction};
16
17/// An agent's account bound to the network it submits to.
18///
19/// Clones share the account's nonce sequence, so transactions submitted
20/// through any clone never collide. Nonces (and so the addresses of deployed
21/// contracts) are assigned in submission order: submissions made one after
22/// another are deterministic for a given seed, while submissions raced from
23/// concurrent tasks are numbered in whatever order the tasks reach them.
24#[derive(Debug, Clone)]
25pub struct Caller {
26 pub(crate) account: Account,
27 pub(crate) network: Network,
28}
29
30impl Caller {
31 pub(crate) fn new(account: Account, network: Network) -> Self {
32 Self { account, network }
33 }
34
35 /// The on-chain wallet address transactions are submitted from.
36 pub fn address(&self) -> ContractAddress {
37 self.account.address()
38 }
39
40 /// Handle to the network transactions are submitted to.
41 pub fn network(&self) -> &Network {
42 &self.network
43 }
44
45 /// Call a deployed contract method from this account, awaiting the
46 /// receipt. The receipt is returned even when execution reverted.
47 pub async fn call(
48 &self,
49 target: ContractAddress,
50 entrypoint: ContractEntrypointID,
51 calldata: &[u8],
52 ) -> Result<TransactionReceipt> {
53 self.submit_call(target, entrypoint, calldata)
54 .await?
55 .await_receipt()
56 .await
57 }
58
59 /// Submit a contract call without waiting for its receipt.
60 pub async fn submit_call(
61 &self,
62 target: ContractAddress,
63 entrypoint: ContractEntrypointID,
64 calldata: &[u8],
65 ) -> Result<SubmittedTransaction> {
66 self.account
67 .submit_call(&self.network, target, entrypoint, calldata)
68 .await
69 }
70}
71
72impl AsRef<Caller> for Caller {
73 fn as_ref(&self) -> &Caller {
74 self
75 }
76}