fuels_programs/calls/traits/
transaction_tuner.rs1use 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 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}