fuels_test_helpers/
accounts.rs

1use std::mem::size_of;
2
3use fuel_crypto::SecretKey;
4use fuels_accounts::wallet::WalletUnlocked;
5use fuels_core::types::errors::Result;
6
7use crate::{
8    node_types::{ChainConfig, NodeConfig},
9    setup_custom_assets_coins, setup_test_provider,
10    wallets_config::*,
11};
12
13/// Launches a local Fuel node, instantiates a provider, and returns a wallet.
14/// The provider and the wallets are instantiated with the default configs.
15/// For more configurable options, see the `launch_custom_provider_and_get_wallets` function.
16/// # Examples
17/// ```
18/// use fuels_test_helpers::launch_provider_and_get_wallet;
19///
20/// async fn single_wallet() -> Result<(), Box<dyn std::error::Error>> {
21///   let wallet = launch_provider_and_get_wallet().await?;
22///   dbg!(wallet.address());
23///   Ok(())
24/// }
25/// ```
26pub async fn launch_provider_and_get_wallet() -> Result<WalletUnlocked> {
27    let mut wallets =
28        launch_custom_provider_and_get_wallets(WalletsConfig::new(Some(1), None, None), None, None)
29            .await?;
30
31    Ok(wallets.pop().expect("should have one wallet"))
32}
33
34/// Launches a custom node and provider, along with a configurable number of wallets.
35///
36/// # Examples
37/// ```
38/// use fuels_test_helpers::launch_custom_provider_and_get_wallets;
39/// use fuels_test_helpers::WalletsConfig;
40///
41/// async fn multiple_wallets() -> Result<(), Box<dyn std::error::Error>> {
42///   let num_wallets = 2;
43///   let num_coins = 1;
44///   let amount = 1;
45///   let config = WalletsConfig::new(Some(num_wallets), Some(num_coins), Some(amount));
46///
47///   let mut wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
48///   let first_wallet = wallets.pop().unwrap();
49///   dbg!(first_wallet.address());
50///   Ok(())
51/// }
52/// ```
53pub async fn launch_custom_provider_and_get_wallets(
54    wallet_config: WalletsConfig,
55    node_config: Option<NodeConfig>,
56    chain_config: Option<ChainConfig>,
57) -> Result<Vec<WalletUnlocked>> {
58    const SIZE_SECRET_KEY: usize = size_of::<SecretKey>();
59    const PADDING_BYTES: usize = SIZE_SECRET_KEY - size_of::<u64>();
60    let mut secret_key: [u8; SIZE_SECRET_KEY] = [0; SIZE_SECRET_KEY];
61
62    let mut wallets: Vec<_> = (1..=wallet_config.num_wallets())
63        .map(|wallet_counter| {
64            secret_key[PADDING_BYTES..].copy_from_slice(&wallet_counter.to_be_bytes());
65
66            WalletUnlocked::new_from_private_key(
67                SecretKey::try_from(secret_key.as_slice())
68                    .expect("This should never happen as we provide a [u8; SIZE_SECRET_KEY] array"),
69                None,
70            )
71        })
72        .collect();
73
74    let all_coins = wallets
75        .iter()
76        .flat_map(|wallet| setup_custom_assets_coins(wallet.address(), wallet_config.assets()))
77        .collect::<Vec<_>>();
78
79    let provider = setup_test_provider(all_coins, vec![], node_config, chain_config).await?;
80
81    for wallet in &mut wallets {
82        wallet.set_provider(provider.clone());
83    }
84
85    Ok(wallets)
86}
87
88#[cfg(test)]
89mod tests {
90    use fuel_core_chain_config::ChainConfig;
91    use fuel_tx::{ConsensusParameters, TxParameters};
92    use fuel_types::AssetId;
93    use fuels_accounts::ViewOnlyAccount;
94    use fuels_core::types::{coin_type::CoinType, errors::Result};
95    use rand::Fill;
96
97    use crate::{launch_custom_provider_and_get_wallets, AssetConfig, WalletsConfig};
98
99    #[tokio::test]
100    async fn test_wallet_config() -> Result<()> {
101        let num_wallets = 2;
102        let num_coins = 3;
103        let amount = 100;
104        let config = WalletsConfig::new(Some(num_wallets), Some(num_coins), Some(amount));
105
106        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
107        let provider = wallets.first().unwrap().try_provider()?;
108        let consensus_parameters = provider.consensus_parameters().await?;
109
110        assert_eq!(wallets.len(), num_wallets as usize);
111
112        for wallet in &wallets {
113            let coins = wallet
114                .get_coins(*consensus_parameters.base_asset_id())
115                .await?;
116
117            assert_eq!(coins.len(), num_coins as usize);
118
119            for coin in &coins {
120                assert_eq!(coin.amount, amount);
121            }
122        }
123        Ok(())
124    }
125
126    #[tokio::test]
127    async fn test_wallet_config_multiple_assets(
128    ) -> std::result::Result<(), Box<dyn std::error::Error>> {
129        let mut rng = rand::thread_rng();
130        let num_wallets = 3;
131
132        let asset_base = AssetConfig {
133            id: AssetId::zeroed(),
134            num_coins: 2,
135            coin_amount: 4,
136        };
137
138        let mut asset_id_1 = AssetId::zeroed();
139        asset_id_1.try_fill(&mut rng)?;
140        let asset_1 = AssetConfig {
141            id: asset_id_1,
142            num_coins: 6,
143            coin_amount: 8,
144        };
145
146        let mut asset_id_2 = AssetId::zeroed();
147        asset_id_2.try_fill(&mut rng)?;
148        let asset_2 = AssetConfig {
149            id: asset_id_2,
150            num_coins: 10,
151            coin_amount: 12,
152        };
153
154        let assets = vec![asset_base, asset_1, asset_2];
155
156        let config = WalletsConfig::new_multiple_assets(num_wallets, assets.clone());
157        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
158        assert_eq!(wallets.len(), num_wallets as usize);
159
160        for asset in assets {
161            for wallet in &wallets {
162                let resources = wallet
163                    .get_spendable_resources(asset.id, asset.num_coins * asset.coin_amount, None)
164                    .await?;
165                assert_eq!(resources.len() as u64, asset.num_coins);
166
167                for resource in resources {
168                    assert_eq!(resource.amount(), asset.coin_amount);
169                    match resource {
170                        CoinType::Coin(coin) => {
171                            assert_eq!(&coin.owner, wallet.address())
172                        }
173                        CoinType::Message(_) => panic!("resources contained messages"),
174                        CoinType::Unknown => panic!("resources contained unknown coins"),
175                    }
176                }
177            }
178        }
179        Ok(())
180    }
181
182    #[tokio::test]
183    async fn generated_wallets_are_deterministic() -> Result<()> {
184        let num_wallets = 32;
185        let num_coins = 1;
186        let amount = 100;
187        let config = WalletsConfig::new(Some(num_wallets), Some(num_coins), Some(amount));
188
189        let wallets = launch_custom_provider_and_get_wallets(config, None, None).await?;
190
191        assert_eq!(
192            wallets.get(31).unwrap().address().to_string(),
193            "fuel1rsjlwjzx0px3zu2al05jdlzp4j5quqzlk7pzyk4g45x6m7r3elzsz9dwh4".to_string()
194        );
195        Ok(())
196    }
197
198    #[tokio::test]
199    async fn generated_wallets_with_custom_chain_config() -> Result<()> {
200        let mut consensus_parameters = ConsensusParameters::default();
201
202        let block_gas_limit = 10_000_000_000;
203        consensus_parameters.set_block_gas_limit(block_gas_limit);
204
205        let max_gas_per_tx = 10_000_000_000;
206        let tx_params = TxParameters::default().with_max_gas_per_tx(max_gas_per_tx);
207        consensus_parameters.set_tx_params(tx_params);
208
209        let chain_config = ChainConfig {
210            consensus_parameters,
211            ..ChainConfig::default()
212        };
213
214        let num_wallets = 4;
215        let num_coins = 3;
216        let coin_amount = 2_000_000_000;
217        let wallets = launch_custom_provider_and_get_wallets(
218            WalletsConfig::new(Some(num_wallets), Some(num_coins), Some(coin_amount)),
219            None,
220            Some(chain_config),
221        )
222        .await?;
223
224        assert_eq!(wallets.len() as u64, num_wallets);
225
226        for wallet in wallets.into_iter() {
227            assert_eq!(
228                wallet
229                    .try_provider()?
230                    .consensus_parameters()
231                    .await?
232                    .tx_params()
233                    .max_gas_per_tx(),
234                max_gas_per_tx
235            );
236            assert_eq!(
237                wallet.get_coins(AssetId::zeroed()).await?.len() as u64,
238                num_coins
239            );
240            assert_eq!(
241                *wallet
242                    .get_balances()
243                    .await?
244                    .get("0000000000000000000000000000000000000000000000000000000000000000")
245                    .expect("failed to get value"),
246                (num_coins * coin_amount) as u128
247            );
248        }
249
250        Ok(())
251    }
252}