From 1e07489b713204e98a7d5a42a7b5d098bf79a523 Mon Sep 17 00:00:00 2001 From: ummarig Date: Tue, 2 Jun 2026 08:35:22 +0000 Subject: [PATCH 1/3] implemented the expensive --- .../stellar/linting/gas_optimization_rules.rs | 50 +++++++++++++++++++ packages/rules/src/stellar/linting/mod.rs | 1 + 2 files changed, 51 insertions(+) diff --git a/packages/rules/src/stellar/linting/gas_optimization_rules.rs b/packages/rules/src/stellar/linting/gas_optimization_rules.rs index dd412bd..8c372f7 100644 --- a/packages/rules/src/stellar/linting/gas_optimization_rules.rs +++ b/packages/rules/src/stellar/linting/gas_optimization_rules.rs @@ -59,6 +59,56 @@ impl SorobanLintRule for StorageReadRule { } } +/// Rule to detect gas-heavy Soroban Map iteration +pub struct MapIterationRule; + +impl SorobanLintRule for MapIterationRule { + fn id(&self) -> &'static str { + "soroban-map-iteration" + } + + fn name(&self) -> &'static str { + "Soroban Map Iteration" + } + + fn description(&self) -> &'static str { + "Detects heavy iteration over Soroban Map collections that can be expensive for large datasets" + } + + fn severity(&self) -> ViolationSeverity { + ViolationSeverity::High + } + + fn check(&self, source: &str, file_path: &str) -> Option> { + let mut violations = Vec::new(); + let lines: Vec<&str> = source.lines().collect(); + + for (i, line) in lines.iter().enumerate() { + if (line.contains("for ") || line.contains("while ")) && + (line.contains(".iter()") || line.contains(".keys(") || line.contains(".values(") || line.contains(".entries(") || line.contains(".range(")) { + let window = lines.iter().skip(i.saturating_sub(8)).take(20).collect::>().join("\n"); + if window.contains("Map<") || window.contains("Map::") || window.contains(".iter()") { + violations.push(RuleViolation { + rule_name: self.id().to_string(), + description: "Detected potentially expensive iteration over a Soroban Map".to_string(), + suggestion: "Use pagination or chunked iteration rather than iterating over the entire Map in one call".to_string(), + line_number: i + 1, + column_number: 0, + variable_name: file_path.to_string(), + severity: self.severity(), + }); + } + } + } + + if violations.is_empty() { + None + } else { + Some(violations) + } + } +} + /// Rule to check for efficient event emission patterns pub struct EventEmissionRule; diff --git a/packages/rules/src/stellar/linting/mod.rs b/packages/rules/src/stellar/linting/mod.rs index af55a59..48486bc 100644 --- a/packages/rules/src/stellar/linting/mod.rs +++ b/packages/rules/src/stellar/linting/mod.rs @@ -30,6 +30,7 @@ impl SorobanLinter { rules.push(Box::new(stellar_sdk_rules::SdkUsageRule)); rules.push(Box::new(stellar_sdk_rules::AddressValidationRule)); rules.push(Box::new(gas_optimization_rules::StorageReadRule)); + rules.push(Box::new(gas_optimization_rules::MapIterationRule)); rules.push(Box::new(gas_optimization_rules::EventEmissionRule)); Self { rules } From a78a50f76d7d6baae317e5cc95ff883a9153ce6f Mon Sep 17 00:00:00 2001 From: ummarig Date: Tue, 2 Jun 2026 08:36:58 +0000 Subject: [PATCH 2/3] implemented the expensive --- .../stellar/linting/gas_optimization_rules.rs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/packages/rules/src/stellar/linting/gas_optimization_rules.rs b/packages/rules/src/stellar/linting/gas_optimization_rules.rs index 8c372f7..8f45ed0 100644 --- a/packages/rules/src/stellar/linting/gas_optimization_rules.rs +++ b/packages/rules/src/stellar/linting/gas_optimization_rules.rs @@ -186,3 +186,49 @@ impl SorobanLintRule for EventEmissionRule { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_map_iteration_rule_detects_map_loop() { + let source = r#" +use soroban_sdk::{contract, contractimpl, contracttype, Address, Env, Map}; + +#[contractimpl] +impl MyContract { + pub fn scan_map(&self, env: Env, data: Map) { + for (key, value) in data.iter() { + let _ = key; + let _ = value; + } + } +} +"#; + + let violations = MapIterationRule.check(&MapIterationRule, source, "test.rs"); + assert!(violations.is_some()); + let violations = violations.unwrap(); + assert!(violations.iter().any(|v| v.rule_name == "soroban-map-iteration")); + } + + #[test] + fn test_map_iteration_rule_ignores_non_map_loops() { + let source = r#" +use soroban_sdk::{contract, contractimpl, contracttype, Address}; + +#[contractimpl] +impl MyContract { + pub fn count(&self) { + for i in 0..10 { + let _ = i; + } + } +} +"#; + + let violations = MapIterationRule.check(&MapIterationRule, source, "test.rs"); + assert!(violations.is_none()); + } +} From 5bb37f7f7b3c84001492b32853fc628c7dd7ae23 Mon Sep 17 00:00:00 2001 From: ummarig Date: Tue, 2 Jun 2026 08:37:59 +0000 Subject: [PATCH 3/3] implemented the expensive --- packages/rules/src/stellar/linting/gas_optimization_rules.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/rules/src/stellar/linting/gas_optimization_rules.rs b/packages/rules/src/stellar/linting/gas_optimization_rules.rs index 8f45ed0..59ef2fd 100644 --- a/packages/rules/src/stellar/linting/gas_optimization_rules.rs +++ b/packages/rules/src/stellar/linting/gas_optimization_rules.rs @@ -87,7 +87,7 @@ impl SorobanLintRule for MapIterationRule { if (line.contains("for ") || line.contains("while ")) && (line.contains(".iter()") || line.contains(".keys(") || line.contains(".values(") || line.contains(".entries(") || line.contains(".range(")) { let window = lines.iter().skip(i.saturating_sub(8)).take(20).collect::>().join("\n"); - if window.contains("Map<") || window.contains("Map::") || window.contains(".iter()") { + if window.contains("Map<") || window.contains("Map::") || window.contains(": Map") { violations.push(RuleViolation { rule_name: self.id().to_string(), description: "Detected potentially expensive iteration over a Soroban Map".to_string(),