Skip to content
This repository was archived by the owner on Dec 2, 2025. It is now read-only.
This repository was archived by the owner on Dec 2, 2025. It is now read-only.

Resolve Contract Circular Dependencies and Architecture Issues #15

@JosueBrenes

Description

@JosueBrenes

Description

Fix the critical circular dependency issues between Pool and Backstop contracts that are preventing proper compilation and testing. Currently, Pool depends on Backstop and Backstop depends on Pool (through Pool Factory), creating a circular dependency that has forced developers to comment out dependencies and exclude contracts from the workspace.

Current Issues Identified

Circular Dependency Chain

Pool Contract → Backstop Contract
     ↑              ↓
Pool Factory ← Backstop Contract

Evidence of Problems

  • Pool and Backstop contracts excluded from main workspace
  • Mock dependencies commented out to avoid circular dependencies
  • Manual WASM copying required in deployment scripts
  • Build process is fragmented and error-prone

What to Fix

  • Resolve Pool ↔ Backstop circular dependency
  • Include all contracts in main workspace
  • Simplify deployment process
  • Enable proper testing with mock contracts
  • Create clean contract interfaces

Acceptance Criteria

  • All contracts compile successfully in single workspace
  • No circular dependencies between contracts
  • Mock contracts can be used for testing
  • Deployment script works without manual WASM copying
  • All tests pass without dependency issues
  • Clean contract interfaces with minimal coupling

Technical Requirements

Files to Modify

  1. Workspace Configuration

    • Path: Cargo.toml (root)
    • Include all contracts in workspace
  2. Pool Contract Dependencies

    • Path: contracts/pool/Cargo.toml
    • Resolve Backstop dependency
  3. Backstop Contract Dependencies

    • Path: contracts/backstop/Cargo.toml
    • Resolve Pool dependency
  4. Deployment Scripts

    • Path: tools/deploy-all.sh
    • Simplify deployment process

Architecture Refactoring Strategy

Option 1: Interface Abstraction

// Create shared trait definitions
pub trait PoolInterface {
    fn get_pool_status(&self) -> PoolStatus;
    fn validate_pool(&self) -> bool;
}

pub trait BackstopInterface {
    fn can_cover_debt(&self, amount: i128) -> bool;
    fn request_coverage(&self, pool_id: &Address, amount: i128);
}

Option 2: Event-Driven Architecture

// Pool emits events instead of direct calls
pub fn liquidate_position(env: Env, user: Address) {
    // Perform liquidation
    emit_liquidation_event(&env, user, liquidated_amount);
}

// Backstop listens to events
pub fn handle_liquidation_event(env: Env, event: LiquidationEvent) {
    // Process backstop coverage
}

Option 3: Mediator Pattern

// Create ProtocolManager contract
pub struct ProtocolManager;

impl ProtocolManager {
    pub fn coordinate_liquidation(
        &self,
        pool: Address,
        backstop: Address,
        user: Address
    ) -> Result<(), Error> {
        // Coordinate between pool and backstop
    }
}

Implementation Details

Phase 1: Dependency Analysis and Interface Design

// contracts/shared/src/interfaces.rs
use soroban_sdk::{Address, Env};

pub trait PoolOperations {
    fn supply(env: Env, from: Address, asset: Address, amount: i128);
    fn borrow(env: Env, from: Address, asset: Address, amount: i128);
    fn liquidate(env: Env, liquidator: Address, user: Address);
}

pub trait BackstopOperations {
    fn deposit(env: Env, from: Address, amount: i128);
    fn cover_loss(env: Env, pool: Address, amount: i128) -> bool;
    fn get_coverage_capacity(env: Env, pool: Address) -> i128;
}

pub trait OracleOperations {
    fn get_price(env: Env, asset: Address) -> Option<i128>;
    fn get_price_with_timestamp(env: Env, asset: Address) -> Option<(i128, u64)>;
}

Phase 2: Contract Refactoring

Pool Contract Refactoring

// contracts/pool/src/contract.rs
use shared::interfaces::{BackstopOperations, OracleOperations};

pub struct Pool;

impl Pool {
    pub fn liquidate(
        env: Env,
        liquidator: Address,
        user: Address,
        backstop_contract: Address
    ) -> Result<(), Error> {
        // Validate liquidation conditions
        let liquidation_amount = self.calculate_liquidation_amount(&env, &user)?;

        // Request backstop coverage through interface
        let backstop_client = BackstopClient::new(&env, &backstop_contract);
        let can_cover = backstop_client.get_coverage_capacity(&pool_address);

        if can_cover >= liquidation_amount {
            // Proceed with liquidation
            self.execute_liquidation(&env, &liquidator, &user, liquidation_amount)?;

            // Notify backstop of potential loss
            backstop_client.cover_loss(&pool_address, liquidation_amount);
        }

        Ok(())
    }
}

Backstop Contract Refactoring

// contracts/backstop/src/contract.rs
pub struct Backstop;

impl BackstopOperations for Backstop {
    fn cover_loss(env: Env, pool: Address, amount: i128) -> bool {
        let pool_data = self.get_pool_data(&env, &pool);
        let available_funds = self.get_available_funds(&env, &pool);

        if available_funds >= amount {
            self.transfer_coverage(&env, &pool, amount);
            true
        } else {
            // Trigger backstop auction or other mechanisms
            self.trigger_backstop_mechanism(&env, &pool, amount);
            false
        }
    }
}

Phase 3: Shared Components

Create Shared Library

// contracts/shared/src/lib.rs
pub mod interfaces;
pub mod types;
pub mod events;
pub mod errors;

// Shared types
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PoolConfig {
    pub oracle: Address,
    pub backstop: Address,
    pub max_utilization: u32,
    pub base_rate: u32,
}

// Shared events
pub fn emit_liquidation(
    env: &Env,
    pool: Address,
    liquidator: Address,
    user: Address,
    amount: i128
) {
    env.events().publish(
        ("liquidation",),
        (pool, liquidator, user, amount)
    );
}

Phase 4: Updated Workspace Configuration

# Root Cargo.toml
[workspace]
members = [
    "contracts/shared",
    "contracts/oracle",
    "contracts/pool-factory",
    "contracts/pool",
    "contracts/backstop",
    "contracts/tbrg-token",
    "contracts/mocks/mock-pool-factory",
    "contracts/mocks/mock-pool",
    "contracts/mocks/moderc3156",
    "testing/test-suites"
]

[workspace.dependencies]
shared = { path = "contracts/shared" }
soroban-sdk = "21.0.0"

Testing Strategy

Unit Tests

#[cfg(test)]
mod tests {
    use super::*;
    use shared::interfaces::*;

    #[test]
    fn test_pool_backstop_interaction() {
        let env = Env::default();
        let pool = PoolClient::new(&env, &env.register_contract(None, Pool));
        let backstop = BackstopClient::new(&env, &env.register_contract(None, Backstop));

        // Test interaction without circular dependency
        assert!(backstop.get_coverage_capacity(&pool.address) > 0);
    }
}

Integration Tests

// testing/test-suites/src/integration_tests.rs
#[test]
fn test_full_liquidation_flow() {
    let env = TestEnv::default();

    // Deploy all contracts
    let contracts = deploy_all_contracts(&env);

    // Test complete liquidation flow
    let result = execute_liquidation_scenario(&env, &contracts);
    assert!(result.is_ok());
}

Deployment Improvements

Simplified Deployment Script

#!/bin/bash
# tools/deploy-all.sh

echo "Building all contracts..."
cargo build --release --target wasm32-unknown-unknown

echo "Optimizing WASM files..."
for contract in oracle pool-factory pool backstop tbrg-token; do
    soroban contract optimize \
        --wasm target/wasm32-unknown-unknown/release/${contract//-/_}.wasm \
        --wasm-out target/wasm32-unknown-unknown/release/${contract//-/_}_optimized.wasm
done

echo "Deploying contracts..."
# Deploy in correct order without manual copying
deploy_contract "oracle"
deploy_contract "pool-factory"
deploy_contract "backstop"
deploy_contract "pool"
deploy_contract "tbrg-token"

Benefits of This Refactoring

Immediate Benefits

  • All contracts compile together
  • Proper testing infrastructure
  • Simplified deployment process
  • Clean dependency management

Long-term Benefits

  • Easier to add new contracts
  • Better modularity and maintainability
  • Reduced deployment complexity
  • Improved testing capabilities

Risk Mitigation

Backward Compatibility

  • Maintain existing contract interfaces
  • Gradual migration approach
  • Comprehensive testing before deployment

Deployment Safety

  • Test on testnet extensively
  • Staged deployment approach
  • Rollback plan if issues arise

Dependencies

  • Soroban SDK latest version
  • Stellar CLI tools
  • Rust toolchain with wasm32 target
  • Contract optimization tools

Definition of Done

  • All contracts included in workspace and compile successfully
  • No circular dependencies in contract architecture
  • All tests pass including integration tests
  • Deployment script works without manual intervention
  • Mock contracts work properly for testing
  • Documentation updated with new architecture
  • Performance benchmarks meet requirements
  • Security audit considerations addressed

Metadata

Metadata

Assignees

No one assigned

    Labels

    good first issueGood for newcomersonlydust-waveContribute to awesome OSS repos during OnlyDust's open source week

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions