diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index 592c792737..95f6e05cb6 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -22,6 +22,7 @@ - [The `abigen!` macro](abigen/the-abigen-macro.md) - [Deploying contracts](./deploying/index.md) - [Configurable constants](./deploying/configurable-constants.md) + - [Storage slots](./deploying/storage-slots.md) - [Interacting with contracts](./deploying/interacting-with-contracts.md) - [The FuelVM Binary file](./deploying/the-fuelvm-binary-file.md) - [Calling contracts](./calling-contracts/index.md) diff --git a/docs/src/deploying/storage-slots.md b/docs/src/deploying/storage-slots.md new file mode 100644 index 0000000000..11bfd3162e --- /dev/null +++ b/docs/src/deploying/storage-slots.md @@ -0,0 +1,13 @@ +# Overriding storage slots + +If you use storage in your contract, the default storage values will be generated in a JSON file (e.g. `my_contract-storage_slots.json`) by the Sway compiler. These are loaded automatically for you when you load a contract binary. If you wish to override some of the defaults, you need to provide the corresponding storage slots manually: + +```rust,ignore +{{#include ../../../examples/contracts/src/lib.rs:storage_slots_override}} +``` + +If you don't have the slot storage file (`my_contract-storage_slots.json` example from above) for some reason, or you don't wish to load any of the default values, you can disable the auto-loading of storage slots: + +```rust,ignore +{{#include ../../../examples/contracts/src/lib.rs:storage_slots_disable_autoload}} +``` diff --git a/examples/contracts/src/lib.rs b/examples/contracts/src/lib.rs index d12a7ba195..a832c32101 100644 --- a/examples/contracts/src/lib.rs +++ b/examples/contracts/src/lib.rs @@ -1,7 +1,12 @@ #[cfg(test)] mod tests { - use fuels::types::errors::{error, Error, Result}; - use fuels::types::Bits256; + use fuels::{ + prelude::{LoadConfiguration, StorageConfiguration}, + types::{ + errors::{error, Error, Result}, + Bits256, + }, + }; #[tokio::test] async fn instantiate_client() -> Result<()> { @@ -138,7 +143,8 @@ mod tests { let key = Bytes32::from([1u8; 32]); let value = Bytes32::from([2u8; 32]); let storage_slot = StorageSlot::new(key, value); - let storage_configuration = StorageConfiguration::from(vec![storage_slot]); + let storage_configuration = + StorageConfiguration::default().add_slot_overrides([storage_slot]); let configuration = LoadConfiguration::default() .with_storage_configuration(storage_configuration) .with_salt(salt); @@ -791,4 +797,33 @@ mod tests { Ok(()) } + + #[tokio::test] + async fn storage_slots_override() -> Result<()> { + { + // ANCHOR: storage_slots_override + use fuels::{programs::contract::Contract, tx::StorageSlot}; + let slot_override = StorageSlot::new([1; 32].into(), [2; 32].into()); + let storage_config = + StorageConfiguration::default().add_slot_overrides([slot_override]); + + let load_config = + LoadConfiguration::default().with_storage_configuration(storage_config); + let _: Result = Contract::load_from("...", load_config); + // ANCHOR_END: storage_slots_override + } + + { + // ANCHOR: storage_slots_disable_autoload + use fuels::programs::contract::Contract; + let storage_config = StorageConfiguration::default().with_autoload(false); + + let load_config = + LoadConfiguration::default().with_storage_configuration(storage_config); + let _: Result = Contract::load_from("...", load_config); + // ANCHOR_END: storage_slots_disable_autoload + } + + Ok(()) + } } diff --git a/examples/cookbook/src/lib.rs b/examples/cookbook/src/lib.rs index ea30f03bee..8b80ee4e4b 100644 --- a/examples/cookbook/src/lib.rs +++ b/examples/cookbook/src/lib.rs @@ -1,9 +1,11 @@ #[cfg(test)] mod tests { - use fuels::types::Bits256; use fuels::{ prelude::Result, - types::transaction_builders::{ScriptTransactionBuilder, TransactionBuilder}, + types::{ + transaction_builders::{ScriptTransactionBuilder, TransactionBuilder}, + Bits256, + }, }; #[tokio::test] diff --git a/packages/fuels-code-gen/src/program_bindings/abigen.rs b/packages/fuels-code-gen/src/program_bindings/abigen.rs index 4870fd3e5e..16fe429416 100644 --- a/packages/fuels-code-gen/src/program_bindings/abigen.rs +++ b/packages/fuels-code-gen/src/program_bindings/abigen.rs @@ -54,10 +54,10 @@ impl Abigen { } fn wasm_paths_hotfix(code: &TokenStream) -> TokenStream { [ - (r#"::\s*std\s*::\s*string"#, "::alloc::string"), - (r#"::\s*std\s*::\s*format"#, "::alloc::format"), - (r#"::\s*std\s*::\s*vec"#, "::alloc::vec"), - (r#"::\s*std\s*::\s*boxed"#, "::alloc::boxed"), + (r"::\s*std\s*::\s*string", "::alloc::string"), + (r"::\s*std\s*::\s*format", "::alloc::format"), + (r"::\s*std\s*::\s*vec", "::alloc::vec"), + (r"::\s*std\s*::\s*boxed", "::alloc::boxed"), ] .map(|(reg_expr_str, substitute)| (Regex::new(reg_expr_str).unwrap(), substitute)) .into_iter() diff --git a/packages/fuels-core/src/types/bech32.rs b/packages/fuels-core/src/types/bech32.rs index 56faf974e1..2d7fab01cf 100644 --- a/packages/fuels-core/src/types/bech32.rs +++ b/packages/fuels-core/src/types/bech32.rs @@ -3,12 +3,14 @@ use std::{ str::FromStr, }; -use crate::types::Bits256; use bech32::{FromBase32, ToBase32, Variant::Bech32m}; use fuel_tx::{Address, Bytes32, ContractId, ContractIdExt}; use fuel_types::AssetId; -use crate::types::errors::{Error, Result}; +use crate::types::{ + errors::{Error, Result}, + Bits256, +}; // Fuel Network human-readable part for bech32 encoding pub const FUEL_BECH32_HRP: &str = "fuel"; diff --git a/packages/fuels-core/src/types/core/sized_ascii_string.rs b/packages/fuels-core/src/types/core/sized_ascii_string.rs index e23dadf50c..1eccc98509 100644 --- a/packages/fuels-core/src/types/core/sized_ascii_string.rs +++ b/packages/fuels-core/src/types/core/sized_ascii_string.rs @@ -1,8 +1,9 @@ use std::fmt::{Debug, Display, Formatter}; -use crate::types::errors::{error, Error, Result}; use serde::{Deserialize, Serialize}; +use crate::types::errors::{error, Error, Result}; + // To be used when interacting with contracts which have string slices in their ABI. // The FuelVM strings only support ascii characters. #[derive(Debug, PartialEq, Clone, Eq)] diff --git a/packages/fuels-core/src/types/param_types.rs b/packages/fuels-core/src/types/param_types.rs index a615f41926..efb20c420e 100644 --- a/packages/fuels-core/src/types/param_types.rs +++ b/packages/fuels-core/src/types/param_types.rs @@ -469,8 +469,6 @@ fn try_str_slice(the_type: &Type) -> Result> { fn try_array(the_type: &Type) -> Result> { if let Some(len) = extract_array_len(&the_type.type_field) { - if the_type.components.len() != 1 {} - return match the_type.components.as_slice() { [single_type] => { let array_type = single_type.try_into()?; diff --git a/packages/fuels-core/src/types/transaction_builders.rs b/packages/fuels-core/src/types/transaction_builders.rs index a7c4184d55..5d5e66e80b 100644 --- a/packages/fuels-core/src/types/transaction_builders.rs +++ b/packages/fuels-core/src/types/transaction_builders.rs @@ -9,9 +9,10 @@ use fuel_tx::{ StorageSlot, Transaction as FuelTransaction, TransactionFee, TxPointer, UniqueIdentifier, Witness, }; -use fuel_types::{bytes::padded_len_usize, Bytes32, Salt}; +use fuel_types::{bytes::padded_len_usize, Bytes32, MemLayout, Salt}; use fuel_vm::{checked_transaction::EstimatePredicates, gas::GasCosts}; +use super::unresolved_bytes::UnresolvedBytes; use crate::{ constants::{BASE_ASSET_ID, WORD_SIZE}, offsets, @@ -27,8 +28,6 @@ use crate::{ }, }; -use super::unresolved_bytes::UnresolvedBytes; - #[derive(Debug, Clone, Default)] struct UnresolvedSignatures { addr_idx_offset_map: HashMap, @@ -373,6 +372,7 @@ impl CreateTransactionBuilder { num_witnesses: u8, consensus_parameters: &ConsensusParameters, ) -> Result { + let num_of_storage_slots = self.storage_slots.len(); let mut tx = FuelTransaction::create( self.gas_price, self.gas_limit, @@ -382,7 +382,7 @@ impl CreateTransactionBuilder { self.storage_slots, resolve_fuel_inputs( self.inputs, - base_offset, + base_offset + num_of_storage_slots * StorageSlot::LEN, num_witnesses, &self.unresolved_signatures, )?, diff --git a/packages/fuels-macros/src/setup_program_test/code_gen.rs b/packages/fuels-macros/src/setup_program_test/code_gen.rs index b7eca0fedc..e52f7638ce 100644 --- a/packages/fuels-macros/src/setup_program_test/code_gen.rs +++ b/packages/fuels-macros/src/setup_program_test/code_gen.rs @@ -120,16 +120,10 @@ fn contract_deploying_code( .get(&command.contract.value()) .expect("Project should be in lookup"); let bin_path = project.bin_path(); - let storage_path = project.storage_path(); quote! { let #contract_instance_name = { - let storage_config = StorageConfiguration::load_from(#storage_path) - .expect("Failed to load storage slots from path"); - let load_config = - LoadConfiguration::default() - .with_storage_configuration(storage_config) - .with_salt([#(#salt),*]); + let load_config = LoadConfiguration::default().with_salt([#(#salt),*]); let loaded_contract = Contract::load_from(#bin_path, load_config).expect("Failed to load the contract"); @@ -212,8 +206,4 @@ impl Project { fn bin_path(&self) -> String { self.compile_file_path(".bin", "the binary file") } - - fn storage_path(&self) -> String { - self.compile_file_path("-storage_slots.json", "the storage slots file") - } } diff --git a/packages/fuels-programs/Cargo.toml b/packages/fuels-programs/Cargo.toml index 378ef0b98a..9a419cbf06 100644 --- a/packages/fuels-programs/Cargo.toml +++ b/packages/fuels-programs/Cargo.toml @@ -31,9 +31,9 @@ strum_macros = { workspace = true } thiserror = { workspace = true, default-features = false } tokio = { workspace = true } +[dev-dependencies] +tempfile = "3.8" + [features] default = ["std"] -std = [ - "fuels-core/std", - "fuels-accounts/std", -] +std = ["fuels-core/std", "fuels-accounts/std"] diff --git a/packages/fuels-programs/src/call_utils.rs b/packages/fuels-programs/src/call_utils.rs index 21de11452f..198907ca73 100644 --- a/packages/fuels-programs/src/call_utils.rs +++ b/packages/fuels-programs/src/call_utils.rs @@ -220,7 +220,7 @@ pub(crate) fn get_instructions( .iter() .zip(&offsets) .flat_map(|(call, offset)| get_single_call_instructions(offset, &call.output_param)) - .chain(op::ret(RegId::ONE).to_bytes().into_iter()) + .chain(op::ret(RegId::ONE).to_bytes()) .collect() } @@ -558,13 +558,13 @@ mod test { Bech32ContractId::new("test", Bytes32::new([1u8; 32])), ]; - let asset_ids = vec![ + let asset_ids = [ AssetId::from([4u8; 32]), AssetId::from([5u8; 32]), AssetId::from([6u8; 32]), ]; - let selectors = vec![[7u8; 8], [8u8; 8], [9u8; 8]]; + let selectors = [[7u8; 8], [8u8; 8], [9u8; 8]]; // Call 2 has multiple inputs, compute_custom_input_offset will be true diff --git a/packages/fuels-programs/src/contract.rs b/packages/fuels-programs/src/contract.rs index 70c531b880..3274d86872 100644 --- a/packages/fuels-programs/src/contract.rs +++ b/packages/fuels-programs/src/contract.rs @@ -1,4 +1,10 @@ -use std::{collections::HashMap, fmt::Debug, fs, marker::PhantomData, path::Path}; +use std::{ + collections::HashMap, + fmt::Debug, + fs, + marker::PhantomData, + path::{Path, PathBuf}, +}; use fuel_tx::{ AssetId, Bytes32, Contract as FuelContract, ContractId, Output, Receipt, Salt, StorageSlot, @@ -19,7 +25,6 @@ use fuels_core::{ }, Configurables, }; -use itertools::Itertools; use crate::{ call_response::FuelCallResponse, @@ -90,44 +95,104 @@ pub trait SettableContract { } /// Configuration for contract storage -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone)] pub struct StorageConfiguration { - slots: Vec, + autoload_storage: bool, + slot_overrides: StorageSlots, +} + +impl Default for StorageConfiguration { + fn default() -> Self { + Self { + autoload_storage: true, + slot_overrides: Default::default(), + } + } } impl StorageConfiguration { - pub fn from(storage_slots: impl IntoIterator) -> Self { + pub fn new(autoload_enabled: bool, slots: impl IntoIterator) -> Self { + let config = Self { + autoload_storage: autoload_enabled, + slot_overrides: Default::default(), + }; + + config.add_slot_overrides(slots) + } + + /// If enabled will try to automatically discover and load the storage configuration from the + /// storage config json file. + pub fn with_autoload(mut self, enabled: bool) -> Self { + self.autoload_storage = enabled; + self + } + + pub fn autoload_enabled(&self) -> bool { + self.autoload_storage + } + + /// Slots added via [`add_slot_overrides`] will override any + /// existing slots with matching keys. + pub fn add_slot_overrides( + mut self, + storage_slots: impl IntoIterator, + ) -> Self { + self.slot_overrides.add_overrides(storage_slots); + self + } + + /// Slots added via [`add_slot_overrides_from_file`] will override any + /// existing slots with matching keys. + /// + /// `path` - path to a JSON file containing the storage slots. + pub fn add_slot_overrides_from_file(mut self, path: impl AsRef) -> Result { + let slots = StorageSlots::load_from_file(path.as_ref())?; + self.slot_overrides.add_overrides(slots.into_iter()); + Ok(self) + } + + pub fn into_slots(self) -> impl Iterator { + self.slot_overrides.into_iter() + } +} + +#[derive(Debug, Clone, Default)] +struct StorageSlots { + storage_slots: HashMap, +} + +impl StorageSlots { + fn from(storage_slots: impl IntoIterator) -> Self { + let pairs = storage_slots.into_iter().map(|slot| (*slot.key(), slot)); Self { - slots: storage_slots.into_iter().unique().collect(), + storage_slots: pairs.collect(), } } - pub fn load_from(storage_path: &str) -> Result { + fn add_overrides(&mut self, storage_slots: impl IntoIterator) -> &mut Self { + let pairs = storage_slots.into_iter().map(|slot| (*slot.key(), slot)); + self.storage_slots.extend(pairs); + self + } + + fn load_from_file(storage_path: impl AsRef) -> Result { + let storage_path = storage_path.as_ref(); validate_path_and_extension(storage_path, "json")?; - let storage_json_string = fs::read_to_string(storage_path).map_err(|_| { + let storage_json_string = std::fs::read_to_string(storage_path).map_err(|e| { error!( InvalidData, - "failed to read storage configuration from: '{storage_path}'" + "failed to read storage slots from: {storage_path:?}. Reason: {e}" ) })?; - Ok(Self { - slots: serde_json::from_str(&storage_json_string)?, - }) - } + let decoded_slots = serde_json::from_str::>(&storage_json_string)?; - pub fn extend(&mut self, storage_slots: impl IntoIterator) { - self.merge(Self::from(storage_slots)) + Ok(StorageSlots::from(decoded_slots)) } - pub fn merge(&mut self, storage_config: StorageConfiguration) { - let slots = std::mem::take(&mut self.slots); - self.slots = slots - .into_iter() - .chain(storage_config.slots) - .unique() - .collect(); + fn into_iter(self) -> impl Iterator { + self.storage_slots.into_values() } } @@ -255,37 +320,70 @@ impl Contract { Ok(self.contract_id.into()) } - pub fn load_from(binary_filepath: &str, configuration: LoadConfiguration) -> Result { + pub fn load_from(binary_filepath: impl AsRef, config: LoadConfiguration) -> Result { + let binary_filepath = binary_filepath.as_ref(); validate_path_and_extension(binary_filepath, "bin")?; let mut binary = fs::read(binary_filepath) - .map_err(|_| error!(InvalidData, "failed to read binary: '{binary_filepath}'"))?; + .map_err(|_| error!(InvalidData, "failed to read binary: {binary_filepath:?}"))?; - configuration.configurables.update_constants_in(&mut binary); + config.configurables.update_constants_in(&mut binary); - Ok(Self::new( - binary, - configuration.salt, - configuration.storage.slots, - )) + let storage_slots = Self::determine_storage_slots(config.storage, binary_filepath)?; + + Ok(Self::new(binary, config.salt, storage_slots)) + } + + fn determine_storage_slots( + storage_config: StorageConfiguration, + binary_filepath: &Path, + ) -> Result> { + let autoload_enabled = storage_config.autoload_enabled(); + let user_overrides = storage_config.into_slots().collect::>(); + let slots = if autoload_enabled { + let mut slots = autoload_storage_slots(binary_filepath)?; + slots.add_overrides(user_overrides); + slots.into_iter().collect() + } else { + user_overrides + }; + + Ok(slots) } } -fn validate_path_and_extension(file_path: &str, extension: &str) -> Result<()> { - let path = Path::new(file_path); +fn autoload_storage_slots(contract_binary: &Path) -> Result { + let storage_file = expected_storage_slots_filepath(contract_binary) + .ok_or_else(|| error!(InvalidData, "Could not determine storage slots file"))?; + + StorageSlots::load_from_file(&storage_file) + .map_err(|_| error!(InvalidData, "Could not autoload storage slots from file: {storage_file:?}. Either provide the file or disable autoloading in StorageConfiguration")) +} + +fn expected_storage_slots_filepath(contract_binary: &Path) -> Option { + let dir = contract_binary.parent()?; + + let binary_filename = contract_binary.file_stem()?.to_str()?; + + Some(dir.join(format!("{binary_filename}-storage_slots.json"))) +} - if !path.exists() { - return Err(error!(InvalidData, "file '{file_path}' does not exist")); +fn validate_path_and_extension(file_path: &Path, extension: &str) -> Result<()> { + if !file_path.exists() { + return Err(error!(InvalidData, "file {file_path:?} does not exist")); } - let path_extension = path - .extension() - .ok_or_else(|| error!(InvalidData, "could not extract extension from: {file_path}"))?; + let path_extension = file_path.extension().ok_or_else(|| { + error!( + InvalidData, + "could not extract extension from: {file_path:?}" + ) + })?; if extension != path_extension { return Err(error!( InvalidData, - "expected `{file_path}` to have '.{extension}' extension" + "expected {file_path:?} to have '.{extension}' extension" )); } @@ -875,3 +973,84 @@ where self } } + +#[cfg(test)] +mod tests { + use std::collections::HashSet; + + use tempfile::tempdir; + + use super::*; + + #[test] + fn merging_overrides_storage_slots() { + // given + let make_slot = |id, value| StorageSlot::new([id; 32].into(), [value; 32].into()); + + let slots = (1..3).map(|id| make_slot(id, 100)); + let original_config = StorageConfiguration::new(false, slots); + + let overlapping_slots = (2..4).map(|id| make_slot(id, 200)); + + // when + let original_config = original_config.add_slot_overrides(overlapping_slots); + + // then + assert_eq!( + HashSet::from_iter(original_config.slot_overrides.into_iter()), + HashSet::from([make_slot(1, 100), make_slot(2, 200), make_slot(3, 200)]) + ); + } + + #[test] + fn autoload_storage_slots() { + // given + let temp_dir = tempdir().unwrap(); + let contract_bin = temp_dir.path().join("my_contract.bin"); + std::fs::write(&contract_bin, "").unwrap(); + + let storage_file = temp_dir.path().join("my_contract-storage_slots.json"); + + let expected_storage_slots = vec![StorageSlot::new([1; 32].into(), [2; 32].into())]; + save_slots(&expected_storage_slots, &storage_file); + + let storage_config = StorageConfiguration::new(true, vec![]); + let load_config = LoadConfiguration::default().with_storage_configuration(storage_config); + + // when + let loaded_contract = Contract::load_from(&contract_bin, load_config).unwrap(); + + // then + assert_eq!(loaded_contract.storage_slots, expected_storage_slots); + } + + #[test] + fn autoload_fails_if_file_missing() { + // given + let temp_dir = tempdir().unwrap(); + let contract_bin = temp_dir.path().join("my_contract.bin"); + std::fs::write(&contract_bin, "").unwrap(); + + let storage_config = StorageConfiguration::new(true, vec![]); + let load_config = LoadConfiguration::default().with_storage_configuration(storage_config); + + // when + let error = Contract::load_from(&contract_bin, load_config) + .expect_err("Should have failed because the storage slots file is missing"); + + // then + let storage_slots_path = temp_dir.path().join("my_contract-storage_slots.json"); + let Error::InvalidData(msg) = error else { + panic!("Expected an error of type InvalidData"); + }; + assert_eq!(msg, format!("Could not autoload storage slots from file: {storage_slots_path:?}. Either provide the file or disable autoloading in StorageConfiguration")); + } + + fn save_slots(slots: &Vec, path: &Path) { + std::fs::write( + path, + serde_json::to_string::>(slots).unwrap(), + ) + .unwrap() + } +} diff --git a/packages/fuels-test-helpers/src/lib.rs b/packages/fuels-test-helpers/src/lib.rs index 7fbf82259d..9924a53084 100644 --- a/packages/fuels-test-helpers/src/lib.rs +++ b/packages/fuels-test-helpers/src/lib.rs @@ -11,9 +11,8 @@ pub use fuel_core::service::DbType; use fuel_core::service::FuelService; #[cfg(feature = "fuel-core-lib")] pub use fuel_core::service::{config::Trigger, Config}; -use fuel_core_chain_config::ChainConfig; #[cfg(feature = "fuel-core-lib")] -use fuel_core_chain_config::StateConfig; +use fuel_core_chain_config::{ChainConfig, StateConfig}; use fuel_core_client::client::FuelClient; use fuel_tx::{Bytes32, ConsensusParameters, UtxoId}; use fuel_types::{AssetId, Nonce}; @@ -61,7 +60,7 @@ pub fn setup_multiple_assets_coins( random_asset_id.try_fill(&mut rng).unwrap(); random_asset_id }) - .chain([BASE_ASSET_ID].into_iter()) + .chain([BASE_ASSET_ID]) .collect::>(); let coins = asset_ids diff --git a/packages/fuels/tests/contracts.rs b/packages/fuels/tests/contracts.rs index 8bc9f64027..bd5aa28602 100644 --- a/packages/fuels/tests/contracts.rs +++ b/packages/fuels/tests/contracts.rs @@ -1119,7 +1119,7 @@ async fn test_add_custom_assets() -> Result<()> { async fn contract_load_error_messages() { { let binary_path = "tests/contracts/contract_test/out/debug/no_file_on_path.bin"; - let expected_error = format!("Invalid data: file '{binary_path}' does not exist"); + let expected_error = format!("Invalid data: file \"{binary_path}\" does not exist"); let error = Contract::load_from(binary_path, LoadConfiguration::default()) .expect_err("Should have failed"); @@ -1129,7 +1129,7 @@ async fn contract_load_error_messages() { { let binary_path = "tests/contracts/contract_test/out/debug/contract_test-abi.json"; let expected_error = - format!("Invalid data: expected `{binary_path}` to have '.bin' extension"); + format!("Invalid data: expected \"{binary_path}\" to have '.bin' extension"); let error = Contract::load_from(binary_path, LoadConfiguration::default()) .expect_err("Should have failed"); @@ -1567,3 +1567,43 @@ async fn test_heap_type_multicall() -> Result<()> { Ok(()) } + +#[tokio::test] +async fn heap_types_correctly_offset_in_create_transactions_w_storage_slots() -> Result<()> { + setup_program_test!( + Wallets("wallet"), + Abigen(Predicate( + name = "MyPredicate", + project = "packages/fuels/tests/types/predicates/predicate_vector" + ),), + ); + + let provider = wallet.try_provider()?.clone(); + let data = MyPredicateEncoder::encode_data(18, 24, vec![2, 4, 42]); + let predicate = Predicate::load_from( + "tests/types/predicates/predicate_vector/out/debug/predicate_vector.bin", + )? + .with_data(data) + .with_provider(provider); + let wallet: WalletUnlocked = wallet; + wallet + .transfer( + predicate.address(), + 10_000, + BASE_ASSET_ID, + TxParameters::default(), + ) + .await?; + + // if the contract is successfully deployed then the predicate was unlocked. This further means + // the offsets were setup correctly since the predicate uses heap types in its arguments. + // Storage slots were loaded automatically by default + Contract::load_from( + "tests/contracts/storage/out/debug/storage.bin", + LoadConfiguration::default(), + )? + .deploy(&predicate, TxParameters::default()) + .await?; + + Ok(()) +} diff --git a/packages/fuels/tests/storage.rs b/packages/fuels/tests/storage.rs index 7a7a88e13e..fef23a746b 100644 --- a/packages/fuels/tests/storage.rs +++ b/packages/fuels/tests/storage.rs @@ -19,7 +19,7 @@ async fn test_storage_initialization() -> Result<()> { let value = Bytes32::from([2u8; 32]); let storage_slot = StorageSlot::new(key, value); let storage_vec = vec![storage_slot.clone()]; - let storage_configuration = StorageConfiguration::from(storage_vec); + let storage_configuration = StorageConfiguration::default().add_slot_overrides(storage_vec); let contract_id = Contract::load_from( "tests/contracts/storage/out/debug/storage.bin", @@ -49,13 +49,10 @@ async fn test_init_storage_automatically() -> Result<()> { )); let wallet = launch_provider_and_get_wallet().await; - let storage_configuration = StorageConfiguration::load_from( - "tests/contracts/storage/out/debug/storage-storage_slots.json", - )?; let contract_id = Contract::load_from( "tests/contracts/storage/out/debug/storage.bin", - LoadConfiguration::default().with_storage_configuration(storage_configuration), + LoadConfiguration::default(), )? .deploy(&wallet, TxParameters::default()) .await?; @@ -89,18 +86,22 @@ async fn test_init_storage_automatically() -> Result<()> { async fn storage_load_error_messages() { { let json_path = "tests/contracts/storage/out/debug/no_file_on_path.json"; - let expected_error = format!("Invalid data: file '{json_path}' does not exist"); + let expected_error = format!("Invalid data: file \"{json_path}\" does not exist"); - let error = StorageConfiguration::load_from(json_path).expect_err("Should have failed"); + let error = StorageConfiguration::default() + .add_slot_overrides_from_file(json_path) + .expect_err("Should have failed"); assert_eq!(error.to_string(), expected_error); } { let json_path = "tests/contracts/storage/out/debug/storage.bin"; let expected_error = - format!("Invalid data: expected `{json_path}` to have '.json' extension"); + format!("Invalid data: expected \"{json_path}\" to have '.json' extension"); - let error = StorageConfiguration::load_from(json_path).expect_err("Should have failed"); + let error = StorageConfiguration::default() + .add_slot_overrides_from_file(json_path) + .expect_err("Should have failed"); assert_eq!(error.to_string(), expected_error); }