1extern crate core;
3
4#[cfg(feature = "fuels-accounts")]
5pub use accounts::*;
6use fuel_tx::{Bytes32, ConsensusParameters, ContractParameters, TxParameters, UtxoId};
7use fuel_types::{AssetId, Nonce};
8use fuels_accounts::provider::Provider;
9use fuels_core::types::{
10 Address,
11 coin::Coin,
12 errors::Result,
13 message::{Message, MessageStatus},
14};
15pub use node_types::*;
16use rand::{Fill, Rng, SeedableRng, rngs::StdRng};
17use utils::{into_coin_configs, into_message_configs};
18pub use wallets_config::*;
19mod node_types;
20
21#[cfg(not(feature = "fuel-core-lib"))]
22pub(crate) mod fuel_bin_service;
23
24#[cfg(feature = "fuels-accounts")]
25mod accounts;
26
27pub use service::*;
28mod service;
29
30mod utils;
31mod wallets_config;
32
33pub fn setup_multiple_assets_coins(
39 owner: Address,
40 num_asset: u64,
41 coins_per_asset: u64,
42 amount_per_coin: u64,
43) -> (Vec<Coin>, Vec<AssetId>) {
44 let mut rng = rand::thread_rng();
45 let asset_ids = (0..(num_asset - 1))
47 .map(|_| {
48 let mut random_asset_id = AssetId::zeroed();
49 random_asset_id
50 .try_fill(&mut rng)
51 .expect("failed to fill with random data");
52 random_asset_id
53 })
54 .chain([AssetId::zeroed()])
55 .collect::<Vec<AssetId>>();
56
57 let coins = asset_ids
58 .iter()
59 .flat_map(|id| setup_single_asset_coins(owner, *id, coins_per_asset, amount_per_coin))
60 .collect::<Vec<Coin>>();
61
62 (coins, asset_ids)
63}
64
65pub fn setup_custom_assets_coins(owner: Address, assets: &[AssetConfig]) -> Vec<Coin> {
67 assets
68 .iter()
69 .flat_map(|asset| {
70 setup_single_asset_coins(owner, asset.id, asset.num_coins, asset.coin_amount)
71 })
72 .collect::<Vec<Coin>>()
73}
74
75pub fn setup_single_asset_coins(
79 owner: Address,
80 asset_id: AssetId,
81 num_coins: u64,
82 amount_per_coin: u64,
83) -> Vec<Coin> {
84 let mut rng = rand::thread_rng();
85
86 let coins: Vec<Coin> = (1..=num_coins)
87 .map(|_i| {
88 let mut r = Bytes32::zeroed();
89 r.try_fill(&mut rng)
90 .expect("failed to fill with random data");
91 let utxo_id = UtxoId::new(r, 0);
92
93 Coin {
94 owner,
95 utxo_id,
96 amount: amount_per_coin,
97 asset_id,
98 }
99 })
100 .collect();
101
102 coins
103}
104
105pub fn setup_single_message(
106 sender: Address,
107 recipient: Address,
108 amount: u64,
109 nonce: Nonce,
110 data: Vec<u8>,
111) -> Message {
112 Message {
113 sender,
114 recipient,
115 nonce,
116 amount,
117 data,
118 da_height: 0,
119 status: MessageStatus::Unspent,
120 }
121}
122
123pub async fn setup_test_provider(
124 coins: Vec<Coin>,
125 messages: Vec<Message>,
126 node_config: Option<NodeConfig>,
127 chain_config: Option<ChainConfig>,
128) -> Result<Provider> {
129 let node_config = node_config.unwrap_or_default();
130 let chain_config = chain_config.unwrap_or_else(testnet_chain_config);
131
132 let coin_configs = into_coin_configs(coins);
133 let message_configs = into_message_configs(messages);
134
135 let state_config = StateConfig {
136 coins: coin_configs,
137 messages: message_configs,
138 ..StateConfig::local_testnet()
139 };
140
141 let srv = FuelService::start(node_config, chain_config, state_config).await?;
142
143 let address = srv.bound_address();
144
145 tokio::spawn(async move {
146 let _own_the_handle = srv;
147 let () = futures::future::pending().await;
148 });
149
150 Provider::from(address).await
151}
152
153fn testnet_chain_config() -> ChainConfig {
155 let mut consensus_parameters = ConsensusParameters::default();
156 let tx_params = TxParameters::default().with_max_size(10_000_000);
157 let _ = consensus_parameters.set_block_transaction_size_limit(10_000_000);
160
161 let contract_params = ContractParameters::default().with_contract_max_size(1_000_000);
162 consensus_parameters.set_tx_params(tx_params);
163 consensus_parameters.set_contract_params(contract_params);
164
165 ChainConfig {
166 consensus_parameters,
167 ..ChainConfig::local_testnet()
168 }
169}
170
171pub fn generate_random_salt() -> [u8; 32] {
172 StdRng::from_entropy().r#gen()
173}
174
175#[cfg(test)]
176mod tests {
177 use std::net::{Ipv4Addr, SocketAddr};
178
179 use fuel_tx::{ConsensusParameters, ContractParameters, FeeParameters, TxParameters};
180
181 use super::*;
182
183 #[tokio::test]
184 async fn test_setup_single_asset_coins() -> Result<()> {
185 let mut rng = rand::thread_rng();
186 let address = rng.r#gen();
187
188 let mut asset_id = AssetId::zeroed();
189 asset_id
190 .try_fill(&mut rng)
191 .expect("failed to fill with random data");
192
193 let number_of_coins = 11;
194 let amount_per_coin = 10;
195 let coins = setup_single_asset_coins(address, asset_id, number_of_coins, amount_per_coin);
196
197 assert_eq!(coins.len() as u64, number_of_coins);
198 for coin in coins {
199 assert_eq!(coin.asset_id, asset_id);
200 assert_eq!(coin.amount, amount_per_coin);
201 assert_eq!(coin.owner, address);
202 }
203
204 Ok(())
205 }
206
207 #[tokio::test]
208 async fn test_setup_multiple_assets_coins() -> Result<()> {
209 let mut rng = rand::thread_rng();
210 let address = rng.r#gen();
211
212 let number_of_assets = 7;
213 let coins_per_asset = 10;
214 let amount_per_coin = 13;
215 let (coins, unique_asset_ids) = setup_multiple_assets_coins(
216 address,
217 number_of_assets,
218 coins_per_asset,
219 amount_per_coin,
220 );
221
222 assert_eq!(coins.len() as u64, number_of_assets * coins_per_asset);
223 assert_eq!(unique_asset_ids.len() as u64, number_of_assets);
224 assert!(
226 unique_asset_ids
227 .iter()
228 .any(|&asset_id| asset_id == AssetId::zeroed())
229 );
230 for asset_id in unique_asset_ids {
231 let coins_asset_id: Vec<Coin> = coins
232 .clone()
233 .into_iter()
234 .filter(|c| c.asset_id == asset_id)
235 .collect();
236 assert_eq!(coins_asset_id.len() as u64, coins_per_asset);
237 for coin in coins_asset_id {
238 assert_eq!(coin.owner, address);
239 assert_eq!(coin.amount, amount_per_coin);
240 }
241 }
242
243 Ok(())
244 }
245
246 #[tokio::test]
247 async fn test_setup_custom_assets_coins() -> Result<()> {
248 let mut rng = rand::thread_rng();
249 let address = rng.r#gen();
250
251 let asset_base = AssetConfig {
252 id: AssetId::zeroed(),
253 num_coins: 2,
254 coin_amount: 4,
255 };
256
257 let mut asset_id_1 = AssetId::zeroed();
258 asset_id_1
259 .try_fill(&mut rng)
260 .expect("failed to fill with random data");
261 let asset_1 = AssetConfig {
262 id: asset_id_1,
263 num_coins: 6,
264 coin_amount: 8,
265 };
266
267 let mut asset_id_2 = AssetId::zeroed();
268 asset_id_2
269 .try_fill(&mut rng)
270 .expect("failed to fill with random data");
271 let asset_2 = AssetConfig {
272 id: asset_id_2,
273 num_coins: 10,
274 coin_amount: 12,
275 };
276
277 let assets = vec![asset_base, asset_1, asset_2];
278 let coins = setup_custom_assets_coins(address, &assets);
279
280 for asset in assets {
281 let coins_asset_id: Vec<Coin> = coins
282 .clone()
283 .into_iter()
284 .filter(|c| c.asset_id == asset.id)
285 .collect();
286 assert_eq!(coins_asset_id.len() as u64, asset.num_coins);
287 for coin in coins_asset_id {
288 assert_eq!(coin.owner, address);
289 assert_eq!(coin.amount, asset.coin_amount);
290 }
291 }
292 Ok(())
293 }
294
295 #[tokio::test]
296 async fn test_setup_test_provider_custom_config() -> Result<()> {
297 let socket = SocketAddr::new(Ipv4Addr::new(127, 0, 0, 1).into(), 4000);
298 let config = NodeConfig {
299 addr: socket,
300 ..NodeConfig::default()
301 };
302
303 let provider = setup_test_provider(vec![], vec![], Some(config.clone()), None).await?;
304 let node_info = provider
305 .node_info()
306 .await
307 .expect("Failed to retrieve node info!");
308
309 assert_eq!(provider.url(), format!("http://127.0.0.1:4000"));
310 assert_eq!(node_info.utxo_validation, config.utxo_validation);
311
312 Ok(())
313 }
314
315 #[tokio::test]
316 async fn test_setup_test_client_consensus_parameters_config() -> Result<()> {
317 let tx_params = TxParameters::default()
318 .with_max_gas_per_tx(2)
319 .with_max_inputs(58);
320 let fee_params = FeeParameters::default().with_gas_per_byte(2);
321 let contract_params = ContractParameters::default().with_max_storage_slots(83);
322
323 let mut consensus_parameters = ConsensusParameters::default();
324 consensus_parameters.set_tx_params(tx_params);
325 consensus_parameters.set_fee_params(fee_params);
326 consensus_parameters.set_contract_params(contract_params);
327
328 let chain_config = ChainConfig {
329 consensus_parameters: consensus_parameters.clone(),
330 ..ChainConfig::default()
331 };
332 let provider = setup_test_provider(vec![], vec![], None, Some(chain_config)).await?;
333
334 let retrieved_parameters = provider.consensus_parameters().await?;
335
336 assert_eq!(retrieved_parameters, consensus_parameters);
337
338 Ok(())
339 }
340
341 #[tokio::test]
342 async fn test_chain_config_and_consensus_parameters() -> Result<()> {
343 let max_inputs = 123;
344 let gas_per_byte = 456;
345
346 let mut consensus_parameters = ConsensusParameters::default();
347
348 let tx_params = TxParameters::default().with_max_inputs(max_inputs);
349 consensus_parameters.set_tx_params(tx_params);
350
351 let fee_params = FeeParameters::default().with_gas_per_byte(gas_per_byte);
352 consensus_parameters.set_fee_params(fee_params);
353
354 let chain_name = "fuel-0".to_string();
355 let chain_config = ChainConfig {
356 chain_name: chain_name.clone(),
357 consensus_parameters,
358 ..ChainConfig::local_testnet()
359 };
360
361 let provider = setup_test_provider(vec![], vec![], None, Some(chain_config)).await?;
362
363 let chain_info = provider.chain_info().await?;
364
365 assert_eq!(chain_info.name, chain_name);
366 assert_eq!(
367 chain_info.consensus_parameters.tx_params().max_inputs(),
368 max_inputs
369 );
370 assert_eq!(
371 chain_info.consensus_parameters.fee_params().gas_per_byte(),
372 gas_per_byte
373 );
374 Ok(())
375 }
376}