1use std::{default::Default, fmt::Debug, path::Path};
2
3use fuel_tx::{Bytes32, ContractId, Salt, StorageSlot};
4use fuels_accounts::Account;
5use fuels_core::{
6 constants::WORD_SIZE,
7 error,
8 types::{
9 bech32::Bech32ContractId,
10 errors::Result,
11 transaction::TxPolicies,
12 transaction_builders::{Blob, CreateTransactionBuilder},
13 },
14 Configurables,
15};
16
17use crate::DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE;
18
19use super::{
20 compute_contract_id_and_state_root, validate_path_and_extension, BlobsNotUploaded, Contract,
21 Loader, StorageConfiguration,
22};
23
24mod code_types {
27 use fuels_core::Configurables;
28
29 #[derive(Debug, Clone, PartialEq)]
30 pub struct Regular {
31 code: Vec<u8>,
32 configurables: Configurables,
33 }
34
35 impl Regular {
36 pub(crate) fn new(code: Vec<u8>, configurables: Configurables) -> Self {
37 Self {
38 code,
39 configurables,
40 }
41 }
42
43 pub(crate) fn with_code(self, code: Vec<u8>) -> Self {
44 Self { code, ..self }
45 }
46
47 pub(crate) fn with_configurables(self, configurables: Configurables) -> Self {
48 Self {
49 configurables,
50 ..self
51 }
52 }
53
54 pub(crate) fn code(&self) -> Vec<u8> {
55 let mut code = self.code.clone();
56 self.configurables.update_constants_in(&mut code);
57 code
58 }
59 }
60}
61pub use code_types::*;
62
63impl Contract<Regular> {
64 pub fn with_code(self, code: Vec<u8>) -> Self {
65 Self {
66 code: self.code.with_code(code),
67 salt: self.salt,
68 storage_slots: self.storage_slots,
69 }
70 }
71
72 pub fn with_configurables(self, configurables: impl Into<Configurables>) -> Self {
73 Self {
74 code: self.code.with_configurables(configurables.into()),
75 ..self
76 }
77 }
78
79 pub fn code(&self) -> Vec<u8> {
80 self.code.code()
81 }
82
83 pub fn contract_id(&self) -> ContractId {
84 self.compute_roots().0
85 }
86
87 pub fn code_root(&self) -> Bytes32 {
88 self.compute_roots().1
89 }
90
91 pub fn state_root(&self) -> Bytes32 {
92 self.compute_roots().2
93 }
94
95 fn compute_roots(&self) -> (ContractId, Bytes32, Bytes32) {
96 compute_contract_id_and_state_root(&self.code(), &self.salt, &self.storage_slots)
97 }
98
99 pub fn load_from(
101 binary_filepath: impl AsRef<Path>,
102 config: LoadConfiguration,
103 ) -> Result<Contract<Regular>> {
104 let binary_filepath = binary_filepath.as_ref();
105 validate_path_and_extension(binary_filepath, "bin")?;
106
107 let binary = std::fs::read(binary_filepath).map_err(|e| {
108 std::io::Error::new(
109 e.kind(),
110 format!("failed to read binary: {binary_filepath:?}: {e}"),
111 )
112 })?;
113
114 let storage_slots = super::determine_storage_slots(config.storage, binary_filepath)?;
115
116 Ok(Contract {
117 code: Regular::new(binary, config.configurables),
118 salt: config.salt,
119 storage_slots,
120 })
121 }
122
123 pub fn regular(
125 code: Vec<u8>,
126 salt: Salt,
127 storage_slots: Vec<StorageSlot>,
128 ) -> Contract<Regular> {
129 Contract {
130 code: Regular::new(code, Configurables::default()),
131 salt,
132 storage_slots,
133 }
134 }
135
136 pub async fn deploy(
140 self,
141 account: &impl Account,
142 tx_policies: TxPolicies,
143 ) -> Result<Bech32ContractId> {
144 let contract_id = self.contract_id();
145 let state_root = self.state_root();
146 let salt = self.salt;
147 let storage_slots = self.storage_slots;
148
149 let mut tb = CreateTransactionBuilder::prepare_contract_deployment(
150 self.code.code(),
151 contract_id,
152 state_root,
153 salt,
154 storage_slots.to_vec(),
155 tx_policies,
156 )
157 .with_max_fee_estimation_tolerance(DEFAULT_MAX_FEE_ESTIMATION_TOLERANCE);
158
159 account.add_witnesses(&mut tb)?;
160 account.adjust_for_fee(&mut tb, 0).await?;
161
162 let provider = account.try_provider()?;
163
164 let tx = tb.build(provider).await?;
165
166 provider
167 .send_transaction_and_await_commit(tx)
168 .await?
169 .check(None)?;
170
171 Ok(contract_id.into())
172 }
173
174 pub async fn deploy_if_not_exists(
177 self,
178 account: &impl Account,
179 tx_policies: TxPolicies,
180 ) -> Result<Bech32ContractId> {
181 let contract_id = Bech32ContractId::from(self.contract_id());
182 let provider = account.try_provider()?;
183 if provider.contract_exists(&contract_id).await? {
184 Ok(contract_id)
185 } else {
186 self.deploy(account, tx_policies).await
187 }
188 }
189
190 pub fn convert_to_loader(
192 self,
193 max_words_per_blob: usize,
194 ) -> Result<Contract<Loader<BlobsNotUploaded>>> {
195 if max_words_per_blob == 0 {
196 return Err(error!(Other, "blob size must be greater than 0"));
197 }
198 let blobs = self
199 .code()
200 .chunks(max_words_per_blob.saturating_mul(WORD_SIZE))
201 .map(|chunk| Blob::new(chunk.to_vec()))
202 .collect();
203
204 Contract::loader_from_blobs(blobs, self.salt, self.storage_slots)
205 }
206
207 pub async fn smart_deploy(
209 self,
210 account: &impl Account,
211 tx_policies: TxPolicies,
212 max_words_per_blob: usize,
213 ) -> Result<Bech32ContractId> {
214 let provider = account.try_provider()?;
215 let max_contract_size = provider
216 .consensus_parameters()
217 .await?
218 .contract_params()
219 .contract_max_size() as usize;
220
221 if self.code().len() <= max_contract_size {
222 self.deploy(account, tx_policies).await
223 } else {
224 self.convert_to_loader(max_words_per_blob)?
225 .deploy(account, tx_policies)
226 .await
227 }
228 }
229}
230
231#[derive(Debug, Clone, Default)]
233pub struct LoadConfiguration {
234 pub(crate) storage: StorageConfiguration,
235 pub(crate) configurables: Configurables,
236 pub(crate) salt: Salt,
237}
238
239impl LoadConfiguration {
240 pub fn new(
241 storage: StorageConfiguration,
242 configurables: impl Into<Configurables>,
243 salt: impl Into<Salt>,
244 ) -> Self {
245 Self {
246 storage,
247 configurables: configurables.into(),
248 salt: salt.into(),
249 }
250 }
251
252 pub fn with_storage_configuration(mut self, storage: StorageConfiguration) -> Self {
253 self.storage = storage;
254 self
255 }
256
257 pub fn with_configurables(mut self, configurables: impl Into<Configurables>) -> Self {
258 self.configurables = configurables.into();
259 self
260 }
261
262 pub fn with_salt(mut self, salt: impl Into<Salt>) -> Self {
263 self.salt = salt.into();
264 self
265 }
266}