fuels_contract/
execution_script.rs1use anyhow::Result;
2use std::fmt::Debug;
3
4use fuel_gql_client::fuel_tx::{Receipt, Transaction};
5
6use fuel_tx::{AssetId, Checkable, ScriptExecutionResult};
7use fuels_core::parameters::TxParameters;
8use fuels_signers::provider::Provider;
9use fuels_signers::{Signer, WalletUnlocked};
10
11use fuels_types::errors::Error;
12
13use std::vec;
14
15use crate::contract::ContractCall;
16use crate::contract_calls_utils::{
17 build_script_data_from_contract_calls, calculate_required_asset_amounts, get_data_offset,
18 get_instructions, get_transaction_inputs_outputs,
19};
20
21#[derive(Debug)]
24pub struct ExecutableFuelCall {
25 pub tx: fuels_core::tx::Script,
26}
27
28impl ExecutableFuelCall {
29 pub fn new(tx: fuels_core::tx::Script) -> Self {
30 Self { tx }
31 }
32
33 pub async fn from_contract_calls(
37 calls: &[ContractCall],
38 tx_parameters: &TxParameters,
39 wallet: &WalletUnlocked,
40 ) -> Result<Self, Error> {
41 let consensus_parameters = wallet.get_provider()?.consensus_parameters().await?;
42 let data_offset = get_data_offset(&consensus_parameters, calls.len());
43
44 let (script_data, call_param_offsets) =
45 build_script_data_from_contract_calls(calls, data_offset, tx_parameters.gas_limit);
46
47 let script = get_instructions(calls, call_param_offsets);
48
49 let required_asset_amounts = calculate_required_asset_amounts(calls);
50 let mut spendable_resources = vec![];
51
52 for (asset_id, amount) in &required_asset_amounts {
54 let resources = wallet.get_spendable_resources(*asset_id, *amount).await?;
55 spendable_resources.extend(resources);
56 }
57
58 let (inputs, outputs) =
59 get_transaction_inputs_outputs(calls, wallet.address(), spendable_resources);
60
61 let mut tx = Transaction::script(
62 tx_parameters.gas_price,
63 tx_parameters.gas_limit,
64 tx_parameters.maturity,
65 script,
66 script_data,
67 inputs,
68 outputs,
69 vec![],
70 );
71
72 let base_asset_amount = required_asset_amounts
73 .iter()
74 .find(|(asset_id, _)| *asset_id == AssetId::default());
75 match base_asset_amount {
76 Some((_, base_amount)) => wallet.add_fee_coins(&mut tx, *base_amount, 0).await?,
77 None => wallet.add_fee_coins(&mut tx, 0, 0).await?,
78 }
79 wallet.sign_transaction(&mut tx).await.unwrap();
80
81 Ok(ExecutableFuelCall::new(tx))
82 }
83
84 pub async fn execute(&self, provider: &Provider) -> Result<Vec<Receipt>, Error> {
86 let chain_info = provider.chain_info().await?;
87
88 self.tx.check_without_signatures(
89 chain_info.latest_block.header.height,
90 &chain_info.consensus_parameters,
91 )?;
92
93 provider.send_transaction(&self.tx).await
94 }
95
96 pub async fn simulate(&self, provider: &Provider) -> Result<Vec<Receipt>, Error> {
98 let chain_info = provider.chain_info().await?;
99
100 self.tx.check_without_signatures(
101 chain_info.latest_block.header.height,
102 &chain_info.consensus_parameters,
103 )?;
104
105 let receipts = provider.dry_run(&self.tx.clone().into()).await?;
106 if receipts
107 .iter()
108 .any(|r|
109 matches!(r, Receipt::ScriptResult { result, .. } if *result != ScriptExecutionResult::Success)
110 ) {
111 return Err(Error::RevertTransactionError(Default::default(), receipts));
112 }
113
114 Ok(receipts)
115 }
116}