1use crate::{
2 call_handler_ext::{
3 TransactionConfig,
4 maybe_return_coins,
5 },
6 utxo_manager::SharedUtxoManager,
7 wallet_ext::{
8 BuilderData,
9 WalletExt,
10 },
11};
12use fuel_core_client::client::FuelClient;
13use fuel_core_types::{
14 fuel_tx::{
15 Create,
16 Output,
17 TxPointer,
18 UniqueIdentifier,
19 Witness,
20 },
21 fuel_types::canonical::Serialize,
22};
23use fuels::{
24 accounts::ViewOnlyAccount,
25 prelude::{
26 Contract,
27 Error as FuelsError,
28 Result as FuelsResult,
29 Wallet,
30 },
31 programs::contract::{
32 DeployResponse,
33 Regular,
34 },
35};
36use std::future::Future;
37
38pub(crate) trait ContractExt {
39 fn almost_sync_deploy(
40 self,
41 account: &Wallet,
42 builder_data: &BuilderData,
43 utxo_manager: &SharedUtxoManager,
44 tx_config: &Option<TransactionConfig>,
45 submit_clients: &[FuelClient],
46 ) -> impl Future<Output = FuelsResult<DeployResponse>>;
47}
48
49impl ContractExt for Contract<Regular> {
50 async fn almost_sync_deploy(
54 self,
55 account: &Wallet,
56 builder_data: &BuilderData,
57 utxo_manager: &SharedUtxoManager,
58 tx_config: &Option<TransactionConfig>,
59 submit_clients: &[FuelClient],
60 ) -> FuelsResult<DeployResponse> {
61 let consensus_parameters = &builder_data.consensus_parameters;
62 let max_fee = builder_data.max_fee();
63 let base_asset_id = *consensus_parameters.base_asset_id();
64 let chain_id = consensus_parameters.chain_id();
65 let secret_key = account.signer().secret_key();
66 let owner = account.address();
67
68 let contract_id = self.contract_id();
69 let state_root = self.state_root();
70 let salt = self.salt();
71 let storage_slots = self.storage_slots();
72 let code = self.code();
73 let bytecode = Witness::from(code);
74
75 let witness_limit = bytecode.size() + crate::wallet_ext::SIGNATURE_MARGIN;
76
77 let mut builder = fuel_core_types::fuel_tx::TransactionBuilder::<Create>::create(
78 bytecode,
79 salt,
80 storage_slots.to_vec(),
81 );
82
83 builder
84 .with_chain_id(consensus_parameters.chain_id())
85 .max_fee_limit(max_fee)
86 .witness_limit(witness_limit as u64);
87
88 let max_fee_coins = tx_config
89 .and_then(|config| config.max_fee_coins)
90 .unwrap_or(usize::MAX);
91
92 let input_coins = {
93 utxo_manager
94 .lock()
95 .await
96 .guaranteed_extract_coins(
97 owner,
98 base_asset_id,
99 max_fee as u128,
100 max_fee_coins,
101 )
102 .map_err(|e| FuelsError::Other(e.to_string()))
103 }?;
104
105 for coin in input_coins.iter() {
106 builder.add_unsigned_coin_input(
107 secret_key,
108 coin.utxo_id,
109 coin.amount,
110 coin.asset_id,
111 TxPointer::default(),
112 );
113 }
114
115 builder.add_output(Output::Change {
116 to: owner,
117 amount: 0,
118 asset_id: base_asset_id,
119 });
120
121 builder.add_output(Output::ContractCreated {
122 contract_id,
123 state_root,
124 });
125
126 let tx = builder.finalize_as_transaction();
127 let tx_id = tx.id(&chain_id);
128
129 let result = match account
130 .send_transaction(chain_id, &tx, submit_clients)
131 .await
132 {
133 Ok(result) => result,
134 Err(e) => {
135 let err_msg = e.to_string();
136 if crate::wallet_ext::is_coin_invalid_error(&err_msg) {
137 tracing::warn!(
138 %tx_id,
139 "Deploy rejected due to invalid coins, \
140 not returning coins to UtxoManager: {err_msg}",
141 );
142 } else {
143 tracing::warn!(
144 %tx_id,
145 "Deploy failed, returning coins to UtxoManager: {err_msg}",
146 );
147 let mut utxo_manager = utxo_manager.lock().await;
148 utxo_manager.load_from_coins_vec(input_coins);
149 }
150 return Err(FuelsError::Other(e.to_string()));
151 }
152 };
153
154 maybe_return_coins(
156 account,
157 &tx,
158 tx_id,
159 tx_config.and_then(|c| c.expiration_height),
160 utxo_manager,
161 );
162
163 let tx_id = Some(result.tx_id);
164
165 {
166 let mut utxo_manager = utxo_manager.lock().await;
167 utxo_manager.load_from_coins_vec(result.known_coins);
168 utxo_manager.load_from_coins_vec(result.dynamic_coins);
169 }
170
171 Ok(DeployResponse {
172 tx_status: Some(result.tx_status.take_success_checked(None)?),
173 tx_id,
174 contract_id,
175 })
176 }
177}