fuels_contract/
script_calls.rs1use crate::{
2 abi_encoder::UnresolvedBytes,
3 call_response::FuelCallResponse,
4 contract::get_decoded_output,
5 contract_calls_utils::get_base_script_offset,
6 execution_script::ExecutableFuelCall,
7 logs::{decode_revert_error, LogDecoder},
8};
9use fuel_gql_client::{
10 fuel_tx::{Output, Receipt, Transaction},
11 fuel_types::bytes::padded_len_usize,
12};
13use fuel_tx::Input;
14use fuels_core::{
15 parameters::{CallParameters, TxParameters},
16 Tokenizable,
17};
18use fuels_signers::{provider::Provider, WalletUnlocked};
19use fuels_types::{errors::Error, param_types::ParamType};
20use std::{fmt::Debug, marker::PhantomData};
21
22#[derive(Debug)]
23pub struct ScriptCall {
25 pub script_binary: Vec<u8>,
26 pub encoded_args: UnresolvedBytes,
27 pub inputs: Vec<Input>,
28 pub outputs: Vec<Output>,
29 pub call_parameters: CallParameters,
31}
32
33impl ScriptCall {
34 pub fn with_outputs(mut self, outputs: Vec<Output>) -> Self {
35 self.outputs = outputs;
36 self
37 }
38
39 pub fn with_inputs(mut self, inputs: Vec<Input>) -> Self {
40 self.inputs = inputs;
41 self
42 }
43}
44
45#[derive(Debug)]
46#[must_use = "script calls do nothing unless you `call` them"]
47pub struct ScriptCallHandler<D> {
49 pub script_call: ScriptCall,
50 pub tx_parameters: TxParameters,
51 pub wallet: WalletUnlocked,
52 pub provider: Provider,
53 pub output_param: ParamType,
54 pub datatype: PhantomData<D>,
55 pub log_decoder: LogDecoder,
56}
57
58impl<D> ScriptCallHandler<D>
59where
60 D: Tokenizable + Debug,
61{
62 pub fn new(
63 script_binary: Vec<u8>,
64 encoded_args: UnresolvedBytes,
65 wallet: WalletUnlocked,
66 provider: Provider,
67 output_param: ParamType,
68 log_decoder: LogDecoder,
69 ) -> Self {
70 let script_call = ScriptCall {
71 script_binary,
72 encoded_args,
73 inputs: vec![],
74 outputs: vec![],
75 call_parameters: Default::default(),
76 };
77 Self {
78 script_call,
79 tx_parameters: TxParameters::default(),
80 wallet,
81 provider,
82 output_param,
83 datatype: PhantomData,
84 log_decoder,
85 }
86 }
87
88 pub fn tx_params(mut self, params: TxParameters) -> Self {
96 self.tx_parameters = params;
97 self
98 }
99
100 pub fn with_outputs(mut self, outputs: Vec<Output>) -> Self {
101 self.script_call = self.script_call.with_outputs(outputs);
102 self
103 }
104
105 pub fn with_inputs(mut self, inputs: Vec<Input>) -> Self {
106 self.script_call = self.script_call.with_inputs(inputs);
107 self
108 }
109
110 async fn compute_script_data(&self) -> Result<Vec<u8>, Error> {
112 let consensus_parameters = self.provider.consensus_parameters().await?;
113 let script_offset = get_base_script_offset(&consensus_parameters)
114 + padded_len_usize(self.script_call.script_binary.len());
115
116 Ok(self.script_call.encoded_args.resolve(script_offset as u64))
117 }
118
119 async fn call_or_simulate(&self, simulate: bool) -> Result<FuelCallResponse<D>, Error> {
125 let mut tx = Transaction::script(
126 self.tx_parameters.gas_price,
127 self.tx_parameters.gas_limit,
128 self.tx_parameters.maturity,
129 self.script_call.script_binary.clone(),
130 self.compute_script_data().await?,
131 self.script_call.inputs.clone(), self.script_call.outputs.clone(), vec![vec![0, 0].into()], );
135 self.wallet.add_fee_coins(&mut tx, 0, 0).await?;
136
137 let tx_execution = ExecutableFuelCall { tx };
138
139 let receipts = if simulate {
140 tx_execution.simulate(&self.provider).await?
141 } else {
142 tx_execution.execute(&self.provider).await?
143 };
144
145 self.get_response(receipts)
146 }
147
148 pub async fn call(self) -> Result<FuelCallResponse<D>, Error> {
150 Self::call_or_simulate(&self, false)
151 .await
152 .map_err(|err| decode_revert_error(err, &self.log_decoder))
153 }
154
155 pub async fn simulate(self) -> Result<FuelCallResponse<D>, Error> {
161 Self::call_or_simulate(&self, true)
162 .await
163 .map_err(|err| decode_revert_error(err, &self.log_decoder))
164 }
165
166 pub fn get_response(&self, mut receipts: Vec<Receipt>) -> Result<FuelCallResponse<D>, Error> {
168 let token = get_decoded_output(&mut receipts, None, &self.output_param)?;
169 Ok(FuelCallResponse::new(
170 D::from_token(token)?,
171 receipts,
172 self.log_decoder.clone(),
173 ))
174 }
175}