fuels-programs 0.77.0

Fuel Rust SDK contracts.
Documentation
use crate::calls::utils::calculate_required_asset_amounts;
use crate::{
    DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE,
    calls::{
        ContractCall, ScriptCall,
        utils::{build_with_tb, sealed, transaction_builder_from_contract_calls},
    },
};
use fuel_tx::ConsensusParameters;
use fuel_types::AssetId;
use fuels_accounts::Account;
use fuels_core::types::input::Input;
use fuels_core::types::{
    errors::{Context, Result, error},
    transaction::{ScriptTransaction, TxPolicies},
    transaction_builders::{
        BuildableTransaction, ScriptTransactionBuilder, TransactionBuilder, VariableOutputPolicy,
    },
};

#[async_trait::async_trait]
pub trait TransactionTuner: sealed::Sealed {
    fn required_assets(&self, base_asset_id: AssetId) -> Vec<(AssetId, u128)>;

    fn transaction_builder<T: Account>(
        &self,
        tx_policies: TxPolicies,
        variable_output_policy: VariableOutputPolicy,
        consensus_parameters: &ConsensusParameters,
        asset_input: Vec<Input>,
        account: &T,
    ) -> Result<ScriptTransactionBuilder>;

    async fn build_tx<T: Account>(
        &self,
        tb: ScriptTransactionBuilder,
        account: &T,
    ) -> Result<ScriptTransaction>;
}

#[async_trait::async_trait]
impl TransactionTuner for ContractCall {
    fn required_assets(&self, base_asset_id: AssetId) -> Vec<(AssetId, u128)> {
        calculate_required_asset_amounts(std::slice::from_ref(self), base_asset_id)
    }

    fn transaction_builder<T: Account>(
        &self,
        tx_policies: TxPolicies,
        variable_output_policy: VariableOutputPolicy,
        consensus_parameters: &ConsensusParameters,
        asset_input: Vec<Input>,
        account: &T,
    ) -> Result<ScriptTransactionBuilder> {
        transaction_builder_from_contract_calls(
            std::slice::from_ref(self),
            tx_policies,
            variable_output_policy,
            consensus_parameters,
            asset_input,
            account,
        )
    }

    async fn build_tx<T: Account>(
        &self,
        tb: ScriptTransactionBuilder,
        account: &T,
    ) -> Result<ScriptTransaction> {
        build_with_tb(std::slice::from_ref(self), tb, account).await
    }
}

#[async_trait::async_trait]
impl TransactionTuner for ScriptCall {
    fn required_assets(&self, _: AssetId) -> Vec<(AssetId, u128)> {
        vec![]
    }

    fn transaction_builder<T: Account>(
        &self,
        tx_policies: TxPolicies,
        variable_output_policy: VariableOutputPolicy,
        _: &ConsensusParameters,
        _: Vec<Input>,
        _account: &T,
    ) -> Result<ScriptTransactionBuilder> {
        let (inputs, outputs) = self.prepare_inputs_outputs()?;

        Ok(ScriptTransactionBuilder::default()
            .with_variable_output_policy(variable_output_policy)
            .with_tx_policies(tx_policies)
            .with_script(self.script_binary.clone())
            .with_script_data(self.compute_script_data()?)
            .with_inputs(inputs)
            .with_outputs(outputs)
            .with_gas_estimation_tolerance(DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE)
            .with_max_fee_estimation_tolerance(DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE))
    }

    async fn build_tx<T: Account>(
        &self,
        mut tb: ScriptTransactionBuilder,
        account: &T,
    ) -> Result<ScriptTransaction> {
        account.add_witnesses(&mut tb)?;
        account
            .adjust_for_fee(&mut tb, 0)
            .await
            .context("failed to adjust inputs to cover for missing base asset")?;

        tb.build(account.try_provider()?).await
    }
}

impl sealed::Sealed for Vec<ContractCall> {}

#[async_trait::async_trait]
impl TransactionTuner for Vec<ContractCall> {
    fn required_assets(&self, base_asset_id: AssetId) -> Vec<(AssetId, u128)> {
        calculate_required_asset_amounts(self, base_asset_id)
    }

    fn transaction_builder<T: Account>(
        &self,
        tx_policies: TxPolicies,
        variable_output_policy: VariableOutputPolicy,
        consensus_parameters: &ConsensusParameters,
        asset_input: Vec<Input>,
        account: &T,
    ) -> Result<ScriptTransactionBuilder> {
        validate_contract_calls(self)?;

        transaction_builder_from_contract_calls(
            self,
            tx_policies,
            variable_output_policy,
            consensus_parameters,
            asset_input,
            account,
        )
    }

    /// Returns the script that executes the contract calls
    async fn build_tx<T: Account>(
        &self,
        tb: ScriptTransactionBuilder,
        account: &T,
    ) -> Result<ScriptTransaction> {
        validate_contract_calls(self)?;

        build_with_tb(self, tb, account).await
    }
}

fn validate_contract_calls(calls: &[ContractCall]) -> Result<()> {
    if calls.is_empty() {
        return Err(error!(
            Other,
            "no calls added. Have you used '.add_calls()'?"
        ));
    }

    Ok(())
}