fuels_programs/calls/traits/
transaction_tuner.rs

1use crate::calls::utils::calculate_required_asset_amounts;
2use crate::{
3    DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
4    calls::{
5        ContractCall, ScriptCall,
6        utils::{build_with_tb, sealed, transaction_builder_from_contract_calls},
7    },
8};
9use fuel_tx::ConsensusParameters;
10use fuel_types::AssetId;
11use fuels_accounts::Account;
12use fuels_core::types::input::Input;
13use fuels_core::types::{
14    errors::{Context, Result, error},
15    transaction::{ScriptTransaction, TxPolicies},
16    transaction_builders::{
17        BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder, VariableOutputPolicy,
18    },
19};
20
21#[async_trait::async_trait]
22pub trait TransactionTuner: sealed::Sealed {
23    fn required_assets(&self, base_asset_id: AssetId) -> Vec<(AssetId, u128)>;
24
25    fn transaction_builder<T: Account>(
26        &self,
27        tx_policies: TxPolicies,
28        variable_output_policy: VariableOutputPolicy,
29        consensus_parameters: &ConsensusParameters,
30        asset_input: Vec<Input>,
31        account: &T,
32    ) -> Result<ScriptTransactionBuilder>;
33
34    async fn build_tx<T: Account>(
35        &self,
36        tb: ScriptTransactionBuilder,
37        account: &T,
38    ) -> Result<ScriptTransaction>;
39}
40
41#[async_trait::async_trait]
42impl TransactionTuner for ContractCall {
43    fn required_assets(&self, base_asset_id: AssetId) -> Vec<(AssetId, u128)> {
44        calculate_required_asset_amounts(std::slice::from_ref(self), base_asset_id)
45    }
46
47    fn transaction_builder<T: Account>(
48        &self,
49        tx_policies: TxPolicies,
50        variable_output_policy: VariableOutputPolicy,
51        consensus_parameters: &ConsensusParameters,
52        asset_input: Vec<Input>,
53        account: &T,
54    ) -> Result<ScriptTransactionBuilder> {
55        transaction_builder_from_contract_calls(
56            std::slice::from_ref(self),
57            tx_policies,
58            variable_output_policy,
59            consensus_parameters,
60            asset_input,
61            account,
62        )
63    }
64
65    async fn build_tx<T: Account>(
66        &self,
67        tb: ScriptTransactionBuilder,
68        account: &T,
69    ) -> Result<ScriptTransaction> {
70        build_with_tb(std::slice::from_ref(self), tb, account).await
71    }
72}
73
74#[async_trait::async_trait]
75impl TransactionTuner for ScriptCall {
76    fn required_assets(&self, _: AssetId) -> Vec<(AssetId, u128)> {
77        vec![]
78    }
79
80    fn transaction_builder<T: Account>(
81        &self,
82        tx_policies: TxPolicies,
83        variable_output_policy: VariableOutputPolicy,
84        _: &ConsensusParameters,
85        _: Vec<Input>,
86        _account: &T,
87    ) -> Result<ScriptTransactionBuilder> {
88        let (inputs, outputs) = self.prepare_inputs_outputs()?;
89
90        Ok(ScriptTransactionBuilder::default()
91            .with_variable_output_policy(variable_output_policy)
92            .with_tx_policies(tx_policies)
93            .with_script(self.script_binary.clone())
94            .with_script_data(self.compute_script_data()?)
95            .with_inputs(inputs)
96            .with_outputs(outputs)
97            .with_gas_estimation_tolerance(DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE)
98            .with_max_fee_estimation_tolerance(DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE))
99    }
100
101    async fn build_tx<T: Account>(
102        &self,
103        mut tb: ScriptTransactionBuilder,
104        account: &T,
105    ) -> Result<ScriptTransaction> {
106        account.add_witnesses(&mut tb)?;
107        account
108            .adjust_for_fee(&mut tb, 0)
109            .await
110            .context("failed to adjust inputs to cover for missing base asset")?;
111
112        tb.build(account.try_provider()?).await
113    }
114}
115
116impl sealed::Sealed for Vec<ContractCall> {}
117
118#[async_trait::async_trait]
119impl TransactionTuner for Vec<ContractCall> {
120    fn required_assets(&self, base_asset_id: AssetId) -> Vec<(AssetId, u128)> {
121        calculate_required_asset_amounts(self, base_asset_id)
122    }
123
124    fn transaction_builder<T: Account>(
125        &self,
126        tx_policies: TxPolicies,
127        variable_output_policy: VariableOutputPolicy,
128        consensus_parameters: &ConsensusParameters,
129        asset_input: Vec<Input>,
130        account: &T,
131    ) -> Result<ScriptTransactionBuilder> {
132        validate_contract_calls(self)?;
133
134        transaction_builder_from_contract_calls(
135            self,
136            tx_policies,
137            variable_output_policy,
138            consensus_parameters,
139            asset_input,
140            account,
141        )
142    }
143
144    /// Returns the script that executes the contract calls
145    async fn build_tx<T: Account>(
146        &self,
147        tb: ScriptTransactionBuilder,
148        account: &T,
149    ) -> Result<ScriptTransaction> {
150        validate_contract_calls(self)?;
151
152        build_with_tb(self, tb, account).await
153    }
154}
155
156fn validate_contract_calls(calls: &[ContractCall]) -> Result<()> {
157    if calls.is_empty() {
158        return Err(error!(
159            Other,
160            "no calls added. Have you used '.add_calls()'?"
161        ));
162    }
163
164    Ok(())
165}