diff --git a/sway-lib-std/src/storage/storage_key.sw b/sway-lib-std/src/storage/storage_key.sw index a4cb12f7d91..71ae74328a7 100644 --- a/sway-lib-std/src/storage/storage_key.sw +++ b/sway-lib-std/src/storage/storage_key.sw @@ -98,6 +98,39 @@ impl StorageKey { write(self.slot, self.offset, value); } + /// Clears the value at `self`. + /// + /// # Number of Storage Accesses + /// + /// * Clears: `1` + /// + /// # Examples + /// + /// ```sway + /// fn foo() { + /// let r: StorageKey = StorageKey { + /// slot: 0x0000000000000000000000000000000000000000000000000000000000000000, + /// offset: 2, + /// field_id: 0x0000000000000000000000000000000000000000000000000000000000000000, + /// }; + /// r.write(42); + /// + /// let cleared = r.clear(); + /// assert(cleared); + /// } + /// ``` + #[storage(write)] + pub fn clear(self) -> bool { + if __size_of::() == 0 { + // If the generic doesn't have a size, this is an empty struct and nothing can be stored at the slot. + // This clears the length value for StorageVec, StorageString, and StorageBytes + // or any other Storage type. + clear::(self.field_id, 0) + } else { + clear::(self.slot, self.offset) + } + } + /// Create a new `StorageKey`. /// /// # Arguments diff --git a/sway-lib-std/src/storage/storage_vec.sw b/sway-lib-std/src/storage/storage_vec.sw index 903536af614..54154362fbf 100644 --- a/sway-lib-std/src/storage/storage_vec.sw +++ b/sway-lib-std/src/storage/storage_vec.sw @@ -481,34 +481,6 @@ impl StorageKey> { read::(self.field_id, 0).unwrap_or(0) == 0 } - /// Sets the len to zero. - /// - /// # Number of Storage Accesses - /// - /// * Clears: `1` - /// - /// # Examples - /// - /// ```sway - /// use std::storage::storage_vec::*; - /// - /// storage { - /// vec: StorageVec = StorageVec {} - /// } - /// - /// fn foo() { - /// assert(0 == storage.vec.len()); - /// storage.vec.push(5); - /// assert(1 == storage.vec.len()); - /// storage.vec.clear(); - /// assert(0 == storage.vec.len()); - /// } - /// ``` - #[storage(write)] - pub fn clear(self) { - let _ = clear::(self.field_id, 0); - } - /// Swaps two elements. /// /// # Arguments diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/static_analysis/cei_pattern_violation_storage_map_and_vec/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/static_analysis/cei_pattern_violation_storage_map_and_vec/test.toml index cc4604fe858..5e559224f27 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/static_analysis/cei_pattern_violation_storage_map_and_vec/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_pass/static_analysis/cei_pattern_violation_storage_map_and_vec/test.toml @@ -2,4 +2,4 @@ category = "compile" # check: $()Storage write after external contract interaction in function or method "withdraw". Consider making all storage writes before calling another contract # check: $()Storage write after external contract interaction in function or method "withdraw". Consider making all storage writes before calling another contract -expected_warnings = 4 +expected_warnings = 5 diff --git a/test/src/sdk-harness/test_projects/storage_access/mod.rs b/test/src/sdk-harness/test_projects/storage_access/mod.rs index 3f835074761..7571a541aaf 100644 --- a/test/src/sdk-harness/test_projects/storage_access/mod.rs +++ b/test/src/sdk-harness/test_projects/storage_access/mod.rs @@ -163,3 +163,18 @@ async fn maps_in_struct_access() { (None, None) ); } + +#[tokio::test] +async fn clears_storage_key() { + let methods = test_storage_access_instance().await.methods(); + + assert_eq!( + methods + .clears_storage_key() + .call() + .await + .unwrap() + .value, + true + ); +} diff --git a/test/src/sdk-harness/test_projects/storage_access/src/main.sw b/test/src/sdk-harness/test_projects/storage_access/src/main.sw index 28267139f9a..4ef3c3b1241 100644 --- a/test/src/sdk-harness/test_projects/storage_access/src/main.sw +++ b/test/src/sdk-harness/test_projects/storage_access/src/main.sw @@ -1,6 +1,6 @@ contract; -use std::hash::*; +use std::{constants::ZERO_B256, hash::*}; struct M { u: b256, @@ -108,6 +108,9 @@ abi ExperimentalStorageTest { #[storage(read, write)] fn map_in_struct_write(key: (u64, u64), value: (u64, u64)); + + #[storage(read, write)] + fn clears_storage_key() -> bool; } impl ExperimentalStorageTest for Contract { @@ -226,4 +229,16 @@ impl ExperimentalStorageTest for Contract { storage.s2.map0.insert(key.0, value.0); storage.s2.map1.insert(key.1, value.1); } + + #[storage(read, write)] + fn clears_storage_key() -> bool { + let key = StorageKey::::new(ZERO_B256, 0, ZERO_B256); + key.write(42); + + assert(key.read() == 42); + let cleared = key.clear(); + assert(cleared); + assert(key.try_read().is_none()); + cleared + } }