fuels_contract/
script_calls.rsuse crate::{
abi_encoder::UnresolvedBytes,
call_response::FuelCallResponse,
contract::get_decoded_output,
contract_calls_utils::get_base_script_offset,
execution_script::ExecutableFuelCall,
logs::{decode_revert_error, LogDecoder},
};
use fuel_gql_client::{
fuel_tx::{Output, Receipt, Transaction},
fuel_types::bytes::padded_len_usize,
};
use fuel_tx::Input;
use fuels_core::{
parameters::{CallParameters, TxParameters},
Tokenizable,
};
use fuels_signers::{provider::Provider, WalletUnlocked};
use fuels_types::{errors::Error, param_types::ParamType};
use std::{fmt::Debug, marker::PhantomData};
#[derive(Debug)]
pub struct ScriptCall {
pub script_binary: Vec<u8>,
pub encoded_args: UnresolvedBytes,
pub inputs: Vec<Input>,
pub outputs: Vec<Output>,
pub call_parameters: CallParameters,
}
impl ScriptCall {
pub fn with_outputs(mut self, outputs: Vec<Output>) -> Self {
self.outputs = outputs;
self
}
pub fn with_inputs(mut self, inputs: Vec<Input>) -> Self {
self.inputs = inputs;
self
}
}
#[derive(Debug)]
#[must_use = "script calls do nothing unless you `call` them"]
pub struct ScriptCallHandler<D> {
pub script_call: ScriptCall,
pub tx_parameters: TxParameters,
pub wallet: WalletUnlocked,
pub provider: Provider,
pub output_param: ParamType,
pub datatype: PhantomData<D>,
pub log_decoder: LogDecoder,
}
impl<D> ScriptCallHandler<D>
where
D: Tokenizable + Debug,
{
pub fn new(
script_binary: Vec<u8>,
encoded_args: UnresolvedBytes,
wallet: WalletUnlocked,
provider: Provider,
output_param: ParamType,
log_decoder: LogDecoder,
) -> Self {
let script_call = ScriptCall {
script_binary,
encoded_args,
inputs: vec![],
outputs: vec![],
call_parameters: Default::default(),
};
Self {
script_call,
tx_parameters: TxParameters::default(),
wallet,
provider,
output_param,
datatype: PhantomData,
log_decoder,
}
}
pub fn tx_params(mut self, params: TxParameters) -> Self {
self.tx_parameters = params;
self
}
pub fn with_outputs(mut self, outputs: Vec<Output>) -> Self {
self.script_call = self.script_call.with_outputs(outputs);
self
}
pub fn with_inputs(mut self, inputs: Vec<Input>) -> Self {
self.script_call = self.script_call.with_inputs(inputs);
self
}
async fn compute_script_data(&self) -> Result<Vec<u8>, Error> {
let consensus_parameters = self.provider.consensus_parameters().await?;
let script_offset = get_base_script_offset(&consensus_parameters)
+ padded_len_usize(self.script_call.script_binary.len());
Ok(self.script_call.encoded_args.resolve(script_offset as u64))
}
async fn call_or_simulate(&self, simulate: bool) -> Result<FuelCallResponse<D>, Error> {
let mut tx = Transaction::script(
self.tx_parameters.gas_price,
self.tx_parameters.gas_limit,
self.tx_parameters.maturity,
self.script_call.script_binary.clone(),
self.compute_script_data().await?,
self.script_call.inputs.clone(), self.script_call.outputs.clone(), vec![vec![0, 0].into()], );
self.wallet.add_fee_coins(&mut tx, 0, 0).await?;
let tx_execution = ExecutableFuelCall { tx };
let receipts = if simulate {
tx_execution.simulate(&self.provider).await?
} else {
tx_execution.execute(&self.provider).await?
};
self.get_response(receipts)
}
pub async fn call(self) -> Result<FuelCallResponse<D>, Error> {
Self::call_or_simulate(&self, false)
.await
.map_err(|err| decode_revert_error(err, &self.log_decoder))
}
pub async fn simulate(self) -> Result<FuelCallResponse<D>, Error> {
Self::call_or_simulate(&self, true)
.await
.map_err(|err| decode_revert_error(err, &self.log_decoder))
}
pub fn get_response(&self, mut receipts: Vec<Receipt>) -> Result<FuelCallResponse<D>, Error> {
let token = get_decoded_output(&mut receipts, None, &self.output_param)?;
Ok(FuelCallResponse::new(
D::from_token(token)?,
receipts,
self.log_decoder.clone(),
))
}
}