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