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