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
4 changes: 4 additions & 0 deletions swaptrade-contracts/counter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ doctest = false
name = "formal_verification_tests"
path = "tests/formal_verification_tests.rs"

[[test]]
name = "trading_comprehensive_test"
path = "tests/trading_comprehensive_test.rs"

[dependencies]
soroban-sdk = { workspace = true }

Expand Down
28 changes: 27 additions & 1 deletion swaptrade-contracts/counter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,24 @@ impl CounterContract {
}

/// Swap tokens using simplified AMM (1:1 XLM <-> USDC-SIM)
pub fn swap(env: Env, from: Symbol, to: Symbol, amount: i128, user: Address) -> Result<i128, ContractError> {
pub fn swap(env: Env, from: Symbol, to: Symbol, amount: i128, min_amount_out: i128, user: Address) -> Result<i128, ContractError> {
require_authenticated_verified_user(&env, &user)?;

// Oracle validation
use crate::oracle::{AggregatorV3Interface, OracleWrapper};
let oracle = OracleWrapper;
let (price, timestamp) = oracle.latest_round_data(&env, (from.clone(), to.clone()))?;

// Basic staleness check (e.g., 5 minutes = 300 seconds)
if env.ledger().timestamp().saturating_sub(timestamp) > 300 {
return Err(ContractError::StalePrice);
}

// Minimal price check (price must be positive)
if price <= 0 {
return Err(ContractError::InvalidPrice);
}

let mut portfolio: Portfolio = env
.storage()
.instance()
Expand Down Expand Up @@ -423,6 +438,13 @@ impl CounterContract {
let fee_amount = (amount * fee_bps as i128) / 10000;
let swap_amount = amount - fee_amount;

// Calculate oracle-based minimum amount
let expected_min_amount = (swap_amount as u128 * price) / crate::trading::PRECISION;
let slippage_tolerance_bps = 500; // 5%
let oracle_min_amount = expected_min_amount * (10000 - slippage_tolerance_bps) / 10000;

let required_min = core::cmp::max(min_amount_out as u128, oracle_min_amount);

// Collect the fee
if fee_amount > 0 {
// Deduct from user
Expand All @@ -448,6 +470,10 @@ impl CounterContract {
swap_amount,
user.clone(),
);

if out_amount < required_min as i128 {
return Err(ContractError::SlippageExceeded);
}

portfolio.record_trade(&env, user.clone());

Expand Down
16 changes: 12 additions & 4 deletions swaptrade-contracts/counter/src/oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,18 @@ pub struct PriceData {
pub timestamp: u64,
}

pub trait PriceFeed {
fn get_price(env: &Env, token_pair: (Symbol, Symbol)) -> Result<u128, ContractError>;
fn last_update_time(env: &Env, token_pair: (Symbol, Symbol)) -> u64;
fn set_price(env: &Env, token_pair: (Symbol, Symbol), price: u128);
pub trait AggregatorV3Interface {
fn latest_round_data(&self, env: &Env, token_pair: (Symbol, Symbol)) -> Result<(i128, u64), ContractError>;
}

pub struct OracleWrapper;

impl AggregatorV3Interface for OracleWrapper {
fn latest_round_data(&self, env: &Env, token_pair: (Symbol, Symbol)) -> Result<(i128, u64), ContractError> {
let price = crate::oracle_adapter::OracleAdapter::get_price(env, token_pair)?;
let timestamp = env.ledger().timestamp();
Ok((price as i128, timestamp))
}
}

fn tolerance_key(pair: &(Symbol, Symbol)) -> (Symbol, Symbol, Symbol) {
Expand Down
2 changes: 1 addition & 1 deletion swaptrade-contracts/counter/trading.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use soroban_sdk::{Env, Symbol, Address, symbol_short};
use crate::portfolio::{Portfolio, Asset};
use crate::oracle::{get_stored_price, ContractError};

const PRECISION: u128 = 1_000_000_000_000_000_000; // 1e18
pub const PRECISION: u128 = 1_000_000_000_000_000_000; // 1e18
const STALE_THRESHOLD_SECONDS: u64 = 600; // 10 minutes
const LP_FEE_BPS: u128 = 30; // 0.3% = 30 basis points

Expand Down