Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ strip = "symbols"
debug-assertions = false
panic = "abort"
codegen-units = 1
lto = true
lto = true
2 changes: 2 additions & 0 deletions contracts/subscription/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ name = "subtrackr_subscription"
path = "src/lib.rs"
crate-type = ["cdylib", "rlib"]

serde = "1.0"

[dependencies]
soroban-sdk = "21.0.0"
subtrackr-types = { path = "../types" }
Expand Down
375 changes: 375 additions & 0 deletions contracts/subscription/src/gas_optimization.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,375 @@
/// Gas Optimization and Targeting Module
/// Provides optimization recommendations and tracks gas targets

use soroban_sdk::{String, Vec, Env};

/// Optimization level
#[derive(Clone, Copy)]
pub enum OptimizationLevel {
Critical, // > 150% of target
High, // 100-150% of target
Medium, // 80-100% of target
Optimal, // < 80% of target
}

impl OptimizationLevel {
pub fn to_string(&self) -> &'static str {
match self {
Self::Critical => "critical",
Self::High => "high",
Self::Medium => "medium",
Self::Optimal => "optimal",
}
}
}

/// Optimization recommendation
#[derive(Clone)]
pub struct OptimizationRecommendation {
pub function_name: String,
pub severity: OptimizationLevel,
pub current_gas: u64,
pub target_gas: u64,
pub potential_savings: u64,
pub recommendation: String,
}

/// Gas optimization targets for each function
pub struct GasOptimizationTargets;

impl GasOptimizationTargets {
/// Get target gas for initialization functions
pub fn initialize_target() -> u64 {
25_000 // Minimal storage setup
}

/// Get target gas for plan creation
pub fn create_plan_target() -> u64 {
75_000 // Multiple storage writes
}

/// Get target gas for subscription
pub fn subscribe_target() -> u64 {
65_000 // Create subscription + index
}

/// Get target gas for charge operation
pub fn charge_subscription_target() -> u64 {
150_000 // Token transfer + storage updates
}

/// Get target gas for cancel subscription
pub fn cancel_subscription_target() -> u64 {
45_000 // Remove from indexes + decrement counts
}

/// Get target gas for pause subscription
pub fn pause_subscription_target() -> u64 {
35_000 // Single storage write
}

/// Get target gas for resume subscription
pub fn resume_subscription_target() -> u64 {
40_000 // Single storage write + time calculation
}

/// Get target gas for request refund
pub fn request_refund_target() -> u64 {
30_000 // Storage write + validation
}

/// Get target gas for approve refund
pub fn approve_refund_target() -> u64 {
35_000 // Storage write + transfer
}

/// Get target gas for request transfer
pub fn request_transfer_target() -> u64 {
25_000 // Storage write
}

/// Get target gas for accept transfer
pub fn accept_transfer_target() -> u64 {
85_000 // Multiple storage operations
}

/// Get target gas for plan query
pub fn get_plan_target() -> u64 {
15_000 // Read from storage
}

/// Get target gas for subscription query
pub fn get_subscription_target() -> u64 {
15_000 // Read from storage
}

/// Get target for user subscriptions query
pub fn get_user_subscriptions_target() -> u64 {
20_000 // Read + iteration
}

/// Get all targets as a map
pub fn all_targets(env: &Env) -> Vec<(String, u64)> {
soroban_sdk::vec![
env,
(String::from_str(env, "initialize"), Self::initialize_target()),
(String::from_str(env, "create_plan"), Self::create_plan_target()),
(String::from_str(env, "subscribe"), Self::subscribe_target()),
(String::from_str(env, "charge_subscription"), Self::charge_subscription_target()),
(String::from_str(env, "cancel_subscription"), Self::cancel_subscription_target()),
(String::from_str(env, "pause_subscription"), Self::pause_subscription_target()),
(String::from_str(env, "resume_subscription"), Self::resume_subscription_target()),
(String::from_str(env, "request_refund"), Self::request_refund_target()),
(String::from_str(env, "approve_refund"), Self::approve_refund_target()),
(String::from_str(env, "request_transfer"), Self::request_transfer_target()),
(String::from_str(env, "accept_transfer"), Self::accept_transfer_target()),
(String::from_str(env, "get_plan"), Self::get_plan_target()),
(String::from_str(env, "get_subscription"), Self::get_subscription_target()),
(String::from_str(env, "get_user_subscriptions"), Self::get_user_subscriptions_target()),
]
}
}

/// Gas optimization strategies and recommendations
pub struct GasOptimizations;

impl GasOptimizations {
/// Get optimization recommendations for a specific function
pub fn get_recommendations_for_function(env: &Env, function_name: &str, current_gas: u64) -> Vec<String> {
let mut recommendations = Vec::new(env);

match function_name {
"create_plan" => {
if current_gas > 100_000 {
recommendations.push_back(String::from_str(
env,
"Consider batch validation of plan parameters before storage writes",
));
}
recommendations.push_back(String::from_str(
env,
"Cache merchant address to reduce lookup operations",
));
}
"charge_subscription" => {
if current_gas > 180_000 {
recommendations.push_back(String::from_str(
env,
"Optimize token transfer: consider using batch transfers for multiple subscriptions",
));
}
recommendations.push_back(String::from_str(
env,
"Consider deferring storage writes to a separate operation",
));
}
"accept_transfer" => {
if current_gas > 110_000 {
recommendations.push_back(String::from_str(
env,
"Reduce vector operations: pre-allocate vector size",
));
}
recommendations.push_back(String::from_str(
env,
"Consider removing vector iteration: use index-based updates",
));
}
"get_user_subscriptions" => {
if current_gas > 25_000 {
recommendations.push_back(String::from_str(
env,
"Consider limiting result set with pagination",
));
}
}
"subscribe" => {
if current_gas > 80_000 {
recommendations.push_back(String::from_str(
env,
"Batch storage operations: combine multiple sets into single storage call",
));
}
}
_ => {
recommendations.push_back(String::from_str(env, "Monitor function for optimization opportunities"));
}
}

recommendations
}

/// Get common optimization strategies
pub fn get_general_optimizations(env: &Env) -> Vec<String> {
let mut optimizations = Vec::new(env);

optimizations.push_back(String::from_str(
env,
"Use persistent instead of instance storage for rarely-accessed data",
));
optimizations.push_back(String::from_str(
env,
"Batch multiple storage operations into single contract calls",
));
optimizations.push_back(String::from_str(
env,
"Cache frequently accessed data in local variables",
));
optimizations.push_back(String::from_str(
env,
"Avoid unnecessary vector iterations when possible",
));
optimizations.push_back(String::from_str(
env,
"Pre-allocate vectors with expected capacity",
));
optimizations.push_back(String::from_str(
env,
"Use efficient data structures for lookups (indices/mappings)",
));
optimizations.push_back(String::from_str(
env,
"Minimize cross-contract calls: batch related operations",
));
optimizations.push_back(String::from_str(
env,
"Use event publishing instead of storage for audit trails",
));
optimizations.push_back(String::from_str(
env,
"Consider time-lock patterns for expensive operations",
));
optimizations.push_back(String::from_str(
env,
"Monitor and break down complex functions into optimizable parts",
));

optimizations
}

/// Categorize gas usage by severity
pub fn categorize_gas_usage(current_gas: u64, target_gas: u64) -> OptimizationLevel {
if current_gas > (target_gas * 150) / 100 {
OptimizationLevel::Critical
} else if current_gas > target_gas {
OptimizationLevel::High
} else if current_gas > (target_gas * 80) / 100 {
OptimizationLevel::Medium
} else {
OptimizationLevel::Optimal
}
}

/// Calculate potential gas savings
pub fn calculate_savings(current_gas: u64, target_gas: u64) -> u64 {
if current_gas > target_gas {
current_gas - target_gas
} else {
0
}
}
}

pub fn get_optimization_priorities(
env: &Env,
gas_metrics: Vec<(String, u64)>,
) -> Vec<(String, u64, String)> {
Vec::new(env)
}

/// Best practices for gas efficiency
pub mod best_practices {
use soroban_sdk::{String, Vec, Env};

pub fn get_storage_best_practices(env: &Env) -> Vec<String> {
let mut practices = Vec::new(env);

practices.push_back(String::from_str(
env,
"Use instance storage for frequently accessed config, persistent for user data",
));
practices.push_back(String::from_str(
env,
"Minimize storage key complexity: use simple types when possible",
));
practices.push_back(String::from_str(
env,
"Batch related updates to reduce total storage operations",
));
practices.push_back(String::from_str(
env,
"Consider denormalization to reduce number of storage reads",
));

practices
}

pub fn get_contract_interaction_best_practices(env: &Env) -> Vec<String> {
let mut practices = Vec::new(env);

practices.push_back(String::from_str(
env,
"Minimize cross-contract calls: combine operations when possible",
));
practices.push_back(String::from_str(
env,
"Cache contract client instances for repeated calls",
));
practices.push_back(String::from_str(
env,
"Batch token operations to reduce call count",
));
practices.push_back(String::from_str(
env,
"Use events for audit trails instead of storage",
));

practices
}

pub fn get_computation_best_practices(env: &Env) -> Vec<String> {
let mut practices = Vec::new(env);

practices.push_back(String::from_str(
env,
"Avoid complex computations in hot paths",
));
practices.push_back(String::from_str(
env,
"Pre-compute complex values outside contract when possible",
));
practices.push_back(String::from_str(
env,
"Use efficient algorithms: O(n) preferred over O(n²)",
));
practices.push_back(String::from_str(
env,
"Short-circuit evaluations to exit early",
));

practices
}

pub fn get_validation_best_practices(env: &Env) -> Vec<String> {
let mut practices = Vec::new(env);

practices.push_back(String::from_str(
env,
"Validate inputs early to fail fast",
));
practices.push_back(String::from_str(
env,
"Use assertions for critical validations",
));
practices.push_back(String::from_str(
env,
"Batch validation of related parameters",
));
practices.push_back(String::from_str(
env,
"Cache validation results when applicable",
));

practices
}
}
Loading
Loading