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
16 changes: 15 additions & 1 deletion contracts/teachlink/src/analytics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//! Bridge monitoring and analytics for bridge operations, validator performance, and chain metrics.

use crate::errors::BridgeError;
use crate::storage::{BRIDGE_METRICS, CHAIN_METRICS, DAILY_VOLUMES, CHAIN_VOLUME_INDEX, CHAIN_METRICS_INDEX};

use crate::types::{BridgeMetrics, ChainMetrics};
use soroban_sdk::{Address, Bytes, Env, Map, Vec};

Expand Down Expand Up @@ -598,3 +598,17 @@ impl AnalyticsManager {
current_time - metrics.last_updated > METRICS_UPDATE_INTERVAL
}
}

impl AnalyticsPort for AnalyticsManager {
fn bridge_metrics(env: &Env) -> BridgeMetrics {
Self::get_bridge_metrics(env)
}

fn health_score(env: &Env) -> u32 {
Self::calculate_health_score(env)
}

fn top_chains_by_volume(env: &Env, max: u32) -> Vec<(u32, i128)> {
Self::get_top_chains_by_volume_bounded(env, max)
}
}
20 changes: 20 additions & 0 deletions contracts/teachlink/src/arbitration.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::errors::EscrowError;
use crate::interfaces::ArbitrationPort;
use crate::storage::{ARBITRATORS, ESCROWS};
use crate::types::{ArbitratorProfile, Escrow, EscrowStatus};
use soroban_sdk::{Address, Env, Map, String, Vec};
Expand Down Expand Up @@ -197,3 +198,22 @@ impl ArbitrationManager {
Ok(())
}
}


impl ArbitrationPort for ArbitrationManager {
fn pick_arbitrator(env: &Env) -> Result<Address, EscrowError> {
Self::pick_arbitrator(env)
}

fn check_stalled(env: &Env, escrow: &Escrow) -> bool {
Self::check_stalled_escrow(env, escrow)
}

fn record_resolution(
env: &Env,
arbitrator: Address,
success: bool,
) -> Result<(), EscrowError> {
Self::update_reputation(env, arbitrator, success)
}
}
17 changes: 17 additions & 0 deletions contracts/teachlink/src/audit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

use crate::errors::BridgeError;
use crate::events::AuditRecordCreatedEvent;
use crate::interfaces::AuditPort;
use crate::storage::{AUDIT_COUNTER, AUDIT_RECORDS, COMPLIANCE_REPORTS};
use crate::types::{AuditRecord, ComplianceReport, OperationType};
use soroban_sdk::{Address, Bytes, Env, Map, Vec};
Expand Down Expand Up @@ -480,3 +481,19 @@ impl AuditManager {
Ok(cleared_count)
}
}

impl AuditPort for AuditManager {
fn create_record(
env: &Env,
op: OperationType,
operator: Address,
details: Bytes,
tx_hash: Bytes,
) -> Result<u64, BridgeError> {
Self::create_audit_record(env, op, operator, details, tx_hash)
}

fn get_count(env: &Env) -> u64 {
Self::get_audit_count(env)
}
}
44 changes: 7 additions & 37 deletions contracts/teachlink/src/backup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
//! and audit trails for compliance. Off-chain systems use events to replicate
//! data; this module records manifests, verification, and RTO recovery metrics.

use crate::audit::AuditManager;
use crate::errors::BridgeError;
use crate::interfaces::AuditPort;
use crate::events::{BackupCreatedEvent, BackupVerifiedEvent, RecoveryExecutedEvent};
use crate::storage::{
BACKUP_COUNTER, BACKUP_MANIFESTS, BACKUP_SCHEDULES, BACKUP_SCHED_CNT, RECOVERY_CNT,
Expand All @@ -19,17 +19,7 @@ pub struct BackupManager;

impl BackupManager {
/// Create a backup manifest (authorized caller). Integrity hash is supplied by off-chain.
/// # Arguments
///
/// * `env` - The environment (if applicable).
///
/// # Examples
///
/// ```rust
/// // Example usage
/// // create_backup(...);
/// ```
pub fn create_backup(

env: &Env,
creator: Address,
integrity_hash: Bytes,
Expand Down Expand Up @@ -73,7 +63,7 @@ impl BackupManager {
.publish(env);

let details = Bytes::from_slice(env, &counter.to_be_bytes());
AuditManager::create_audit_record(
Au::create_record(
env,
OperationType::BackupCreated,
creator,
Expand Down Expand Up @@ -109,17 +99,7 @@ impl BackupManager {
}

/// Verify backup integrity (compare expected hash to stored). Emit event and audit.
/// # Arguments
///
/// * `env` - The environment (if applicable).
///
/// # Examples
///
/// ```rust
/// // Example usage
/// // verify_backup(...);
/// ```
pub fn verify_backup(

env: &Env,
backup_id: u64,
verifier: Address,
Expand All @@ -140,7 +120,7 @@ impl BackupManager {
.publish(env);

let details = Bytes::from_slice(env, &[if valid { 1u8 } else { 0u8 }]);
AuditManager::create_audit_record(
Au::create_record(
env,
OperationType::BackupVerified,
verifier,
Expand Down Expand Up @@ -232,17 +212,7 @@ impl BackupManager {
}

/// Record a recovery execution (RTO tracking and audit trail)
/// # Arguments
///
/// * `env` - The environment (if applicable).
///
/// # Examples
///
/// ```rust
/// // Example usage
/// // record_recovery(...);
/// ```
pub fn record_recovery(

env: &Env,
backup_id: u64,
executed_by: Address,
Expand Down Expand Up @@ -286,7 +256,7 @@ impl BackupManager {
.publish(env);

let details = Bytes::from_slice(env, &recovery_duration_secs.to_be_bytes());
AuditManager::create_audit_record(
Au::create_record(
env,
OperationType::RecoveryExecuted,
executed_by,
Expand Down
113 changes: 27 additions & 86 deletions contracts/teachlink/src/escrow.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
use crate::arbitration::ArbitrationManager;
use crate::errors::EscrowError;
use crate::escrow_analytics::EscrowAnalyticsManager;
use crate::events::{
EscrowApprovedEvent, EscrowCreatedEvent, EscrowDisputedEvent, EscrowRefundedEvent,
EscrowReleasedEvent, EscrowResolvedEvent,
};
use crate::insurance::InsuranceManager;
use crate::interfaces::{ArbitrationPort, EscrowObserver, InsurancePort};
use crate::storage::{ESCROWS, ESCROW_COUNT};
use crate::types::{DisputeOutcome, Escrow, EscrowApprovalKey, EscrowSigner, EscrowStatus};
use crate::validation::EscrowValidator;
use soroban_sdk::{symbol_short, vec, Address, Bytes, Env, IntoVal, Map, Vec};
use soroban_sdk::{symbol_short, vec, Address, Bytes, Env, IntoVal, Vec};

pub struct EscrowManager;

impl EscrowManager {
/// Standard API for create_escrow
///
/// # Arguments
///
/// * `env` - The environment (if applicable).
///
/// # Examples
///
/// ```rust
/// // Example usage
/// // create_escrow(...);
/// ```
pub fn create_escrow(

env: &Env,
depositor: Address,
beneficiary: Address,
Expand Down Expand Up @@ -64,15 +50,15 @@ impl EscrowManager {
],
);

// Process insurance premium
// Process insurance premium via injected InsurancePort
if env
.storage()
.instance()
.has(&crate::storage::INSURANCE_POOL)
{
let premium = InsuranceManager::calculate_premium(env, amount);
let premium = I::calculate_premium(env, amount);
if premium > 0 {
InsuranceManager::pay_premium_internal(env, depositor.clone(), premium)?;
I::pay_premium(env, depositor.clone(), premium)?;
}
}

Expand All @@ -98,11 +84,11 @@ impl EscrowManager {
dispute_reason: None,
};

let mut escrows = Self::load_escrows(env);
escrows.set(escrow_count, escrow.clone());
env.storage().instance().set(&ESCROWS, &escrows);
env.storage()
.persistent()
.set(&(ESCROWS, escrow_count), &escrow);

EscrowAnalyticsManager::update_creation(env, amount);
Obs::on_created(env, amount);

EscrowCreatedEvent { escrow }.publish(env);

Expand Down Expand Up @@ -283,19 +269,7 @@ impl EscrowManager {
Ok(())
}

/// Standard API for dispute
///
/// # Arguments
///
/// * `env` - The environment (if applicable).
///
/// # Examples
///
/// ```rust
/// // Example usage
/// // dispute(...);
/// ```
pub fn dispute(

env: &Env,
escrow_id: u64,
disputer: Address,
Expand All @@ -312,14 +286,14 @@ impl EscrowManager {

// If arbitrator is default (zero address), pick a professional one
if Self::arbitrator_is_empty(env, &escrow.arbitrator) {
escrow.arbitrator = ArbitrationManager::pick_arbitrator(env)?;
escrow.arbitrator = A::pick_arbitrator(env)?;
}

escrow.status = EscrowStatus::Disputed;
escrow.dispute_reason = Some(reason.clone());
Self::save_escrow(env, escrow_id, escrow);

EscrowAnalyticsManager::update_dispute(env);
Obs::on_disputed(env);

EscrowDisputedEvent {
escrow_id,
Expand All @@ -331,19 +305,7 @@ impl EscrowManager {
Ok(())
}

/// Standard API for resolve
///
/// # Arguments
///
/// * `env` - The environment (if applicable).
///
/// # Examples
///
/// ```rust
/// // Example usage
/// // resolve(...);
/// ```
pub fn resolve(

env: &Env,
escrow_id: u64,
arbitrator: Address,
Expand Down Expand Up @@ -379,12 +341,12 @@ impl EscrowManager {

escrow.status = new_status.clone();

ArbitrationManager::update_reputation(env, arbitrator, true)?;
A::record_resolution(env, arbitrator, true)?;

let now = env.ledger().timestamp();
let created_at = escrow.created_at;
Self::save_escrow(env, escrow_id, escrow);
EscrowAnalyticsManager::update_resolution(env, now - created_at);
Obs::on_resolved(env, now - created_at);

EscrowResolvedEvent {
escrow_id,
Expand All @@ -396,28 +358,12 @@ impl EscrowManager {
Ok(())
}

/// Standard API for auto_check_dispute
///
/// # Arguments
///
/// * `env` - The environment (if applicable).
///
/// # Returns
///
/// * The return value of the function.
///
/// # Examples
///
/// ```rust
/// // Example usage
/// // auto_check_dispute(...);
/// ```
pub fn auto_check_dispute(env: &Env, escrow_id: u64) -> Result<(), EscrowError> {

let mut escrow = Self::load_escrow(env, escrow_id)?;
if ArbitrationManager::check_stalled_escrow(env, &escrow) {
if A::check_stalled(env, &escrow) {
escrow.status = EscrowStatus::Disputed;
escrow.dispute_reason = Some(Bytes::from_slice(env, b"Automated stall detection"));
escrow.arbitrator = ArbitrationManager::pick_arbitrator(env)?;
escrow.arbitrator = A::pick_arbitrator(env)?;
Self::save_escrow(env, escrow_id, escrow);

EscrowDisputedEvent {
Expand Down Expand Up @@ -449,7 +395,7 @@ impl EscrowManager {
/// // get_escrow(...);
/// ```
pub fn get_escrow(env: &Env, escrow_id: u64) -> Option<Escrow> {
Self::load_escrows(env).get(escrow_id)
env.storage().persistent().get(&(ESCROWS, escrow_id))
}

/// Standard API for get_escrow_count
Expand Down Expand Up @@ -520,22 +466,17 @@ impl EscrowManager {
*arbitrator == env.current_contract_address()
}

fn load_escrows(env: &Env) -> Map<u64, Escrow> {
env.storage()
.instance()
.get(&ESCROWS)
.unwrap_or_else(|| Map::new(env))
}

fn load_escrow(env: &Env, escrow_id: u64) -> Result<Escrow, EscrowError> {
let escrows = Self::load_escrows(env);
escrows.get(escrow_id).ok_or(EscrowError::EscrowNotFound)
env.storage()
.persistent()
.get(&(ESCROWS, escrow_id))
.ok_or(EscrowError::EscrowNotFound)
}

fn save_escrow(env: &Env, escrow_id: u64, escrow: Escrow) {
let mut escrows = Self::load_escrows(env);
escrows.set(escrow_id, escrow);
env.storage().instance().set(&ESCROWS, &escrows);
env.storage()
.persistent()
.set(&(ESCROWS, escrow_id), &escrow);
}

fn transfer_from_contract(env: &Env, token: &Address, to: &Address, amount: i128) {
Expand Down
Loading
Loading