Skip to content

Commit 13815d4

Browse files
authored
fix: correct per-variable bytes/string identification and remove nested scan (#12603)
1 parent 6f958a2 commit 13815d4

File tree

1 file changed

+58
-57
lines changed

1 file changed

+58
-57
lines changed

crates/common/src/slot_identifier.rs

Lines changed: 58 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,13 @@ impl SlotIdentifier {
488488
};
489489
// Get the base slot value from storage_values
490490
if let Some(base_value) = storage_values.get(&base_slot)
491-
&& let Some(info) = self.handle_bytes_string(slot_u256, &slot_str, base_value)
491+
&& let Some(info) = self.handle_bytes_string(
492+
storage,
493+
storage_type,
494+
slot_u256,
495+
&slot_str,
496+
base_value,
497+
)
492498
{
493499
return Some(info);
494500
}
@@ -850,80 +856,75 @@ impl SlotIdentifier {
850856
/// bytes/strings)
851857
fn handle_bytes_string(
852858
&self,
859+
storage: &Storage,
860+
storage_type: &StorageType,
853861
slot: U256,
854862
slot_str: &str,
855863
base_slot_value: &B256,
856864
) -> Option<SlotInfo> {
857-
for storage in &self.storage_layout.storage {
858-
// Get the type information and base slot
859-
let Some(storage_type) = self.storage_layout.types.get(&storage.storage_type) else {
860-
continue;
865+
// Only handle bytes/string encoded variables for this specific storage entry
866+
if storage_type.encoding != ENCODING_BYTES {
867+
return None;
868+
}
869+
870+
// Check if this is the main slot for this variable
871+
let base_slot = U256::from_str(&storage.slot).ok()?;
872+
if slot == base_slot {
873+
// Parse the type to get the correct DynSolType
874+
let dyn_type = if storage_type.label == "string" {
875+
DynSolType::String
876+
} else if storage_type.label == "bytes" {
877+
DynSolType::Bytes
878+
} else {
879+
return None;
861880
};
862881

863-
// Skip if not bytes or string encoding
864-
if storage_type.encoding != ENCODING_BYTES {
865-
continue;
866-
}
882+
return Some(SlotInfo {
883+
label: storage.label.clone(),
884+
slot_type: StorageTypeInfo {
885+
label: storage_type.label.clone(),
886+
dyn_sol_type: dyn_type,
887+
},
888+
offset: storage.offset,
889+
slot: slot_str.to_string(),
890+
members: None,
891+
decoded: None,
892+
keys: None,
893+
});
894+
}
867895

868-
// Check if this is the main slot
869-
let base_slot = U256::from_str(&storage.slot).ok()?;
870-
if slot == base_slot {
871-
// Parse the type to get the correct DynSolType
872-
let dyn_type = if storage_type.label == "string" {
873-
DynSolType::String
874-
} else if storage_type.label == "bytes" {
875-
DynSolType::Bytes
876-
} else {
877-
continue;
878-
};
896+
// Check if it could be a data slot for this long bytes/string
897+
// Calculate where data slots would start for this variable
898+
let data_start =
899+
U256::from_be_bytes(alloy_primitives::keccak256(base_slot.to_be_bytes::<32>()).0);
900+
901+
// Get the length from the base slot value to calculate exact number of slots
902+
// For long bytes/strings, the length is stored as (length * 2 + 1) in the base slot
903+
let length_byte = base_slot_value.0[31];
904+
if length_byte & 1 == 1 {
905+
// It's a long bytes/string
906+
let length = U256::from_be_bytes(base_slot_value.0) >> 1;
907+
// Calculate number of slots needed (round up)
908+
let num_slots = (length + U256::from(31)) / U256::from(32);
909+
910+
// Check if our slot is within the data region
911+
if slot >= data_start && slot < data_start + num_slots {
912+
let slot_index = (slot - data_start).to::<usize>();
879913

880914
return Some(SlotInfo {
881-
label: storage.label.clone(),
915+
label: format!("{}[{}]", storage.label, slot_index),
882916
slot_type: StorageTypeInfo {
883917
label: storage_type.label.clone(),
884-
dyn_sol_type: dyn_type,
918+
// Type is assigned as FixedBytes(32) for data slots
919+
dyn_sol_type: DynSolType::FixedBytes(32),
885920
},
886-
offset: storage.offset,
921+
offset: 0,
887922
slot: slot_str.to_string(),
888923
members: None,
889924
decoded: None,
890925
keys: None,
891926
});
892927
}
893-
894-
// Check if it could be a data slot for this long bytes/string
895-
// Calculate where data slots would start for this variable
896-
let data_start =
897-
U256::from_be_bytes(alloy_primitives::keccak256(base_slot.to_be_bytes::<32>()).0);
898-
899-
// Get the length from the base slot value to calculate exact number of slots
900-
// For long bytes/strings, the length is stored as (length * 2 + 1) in the base slot
901-
let length_byte = base_slot_value.0[31];
902-
if length_byte & 1 == 1 {
903-
// It's a long bytes/string
904-
let length = U256::from_be_bytes(base_slot_value.0) >> 1;
905-
// Calculate number of slots needed (round up)
906-
let num_slots = (length + U256::from(31)) / U256::from(32);
907-
908-
// Check if our slot is within the data region
909-
if slot >= data_start && slot < data_start + num_slots {
910-
let slot_index = (slot - data_start).to::<usize>();
911-
912-
return Some(SlotInfo {
913-
label: format!("{}[{}]", storage.label, slot_index),
914-
slot_type: StorageTypeInfo {
915-
label: storage_type.label.clone(),
916-
// Type is assigned as FixedBytes(32) for data slots
917-
dyn_sol_type: DynSolType::FixedBytes(32),
918-
},
919-
offset: 0,
920-
slot: slot_str.to_string(),
921-
members: None,
922-
decoded: None,
923-
keys: None,
924-
});
925-
}
926-
}
927928
}
928929

929930
None

0 commit comments

Comments
 (0)