Skip to content
Open
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
51 changes: 51 additions & 0 deletions contracts/compound_fees/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use soroban_sdk::{contract, contractimpl, Address, Env};
use crate::error::VaultError; // Assuming a custom error enum exists
use crate::storage; // Assuming standard data storage accessors

#[contract]
pub struct FeeVaultContract;

#[contractimpl]
impl FeeVaultContract {
/// Compounds pending fees from the protocol fee wrapper into the main vault pool.
pub fn compound_fees(env: Env, caller: Address) -> Result<(), VaultError> {
// 1. Authorize caller (can be restricted to admin/relayer or kept permissionless)
caller.require_auth();

// 2. Fetch configuration and addresses from storage
let admin = storage::get_admin(&env)?;
if caller != admin {
return Err(VaultError::Unauthorized);
}

let fee_contract = storage::get_fee_contract(&env)?;
let underlying_token = storage::get_underlying_token(&env)?;
let vault_address = env.current_contract_address();

// 3. Invoke the fee contract to harvest/pull pending tokens
// Assuming the fee contract exposes a function like `harvest_fees` or `claim`
// that transfers tokens directly to the vault.
let fee_client = FeeContractClient::new(&env, &fee_contract);
let pending_amount: i128 = fee_client.harvest(&vault_address);

if pending_amount <= 0 {
return Ok(()); // Nothing to compound
}

// 4. Update vault's underlying balance tracker
let current_balance = storage::get_total_underlying(&env);
let new_balance = current_balance
.checked_add(pending_amount)
.ok_or(VaultError::MathOverflow)?;

storage::set_total_underlying(&env, &new_balance);

// 5. Emit compounding event
env.events().publish(
(symbol_short!("compound"), caller),
pending_amount,
);

Ok(())
}
}
23 changes: 23 additions & 0 deletions contracts/compound_fees/src/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#[cfg(test)]
mod test {
use super::*;
use soroban_sdk::Env;

#[test]
fn test_compound_fees_success() {
let env = Env::default();
env.mock_all_auths();

// Setup contract, token client, and mock fee contract balances
// ...

// Assert balance updates correctly after compounding
}

#[test]
#[should_panic(expected = "Unauthorized")]
fn test_compound_fees_unauthorized() {
let env = Env::default();
// Test execution with non-admin caller
}
}
36 changes: 36 additions & 0 deletions contracts/flash_loan_guard/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use soroban_sdk::{contract, contractimpl, contracttype, Address, Env};

#[derive(Clone)]
#[contracttype]
pub enum DataKey {
DepositBlock(Address),
}

@contract
pub struct FlashLoanGuardContract;

@contractimpl
impl FlashLoanGuardContract {
pub fn deposit(env: Env, user: Address) {
user.require_auth();
let current_block = env.ledger().sequence();
env.storage().persistent().set(&DataKey::DepositBlock(user.clone()), &current_block);
}

pub fn withdraw(env: Env, user: Address) {
user.require_auth();
let current_block = env.ledger().sequence();

let deposit_block: u32 = env.storage()
.persistent()
.get(&DataKey::DepositBlock(user.clone()))
.unwrap_or(0);

if current_block <= deposit_block {
panic!("FlashLoanReentrancy: withdrawal prohibited in the same ledger block as deposit");
}

// Proceed with withdrawal logic...
env.storage().persistent().remove(&DataKey::DepositBlock(user));
}
}
37 changes: 37 additions & 0 deletions contracts/flash_loan_guard/src/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#[cfg(test)]
mod test {
use super::*;
use soroban_sdk::{Env, Address};

#[test]
#[should_panic(expected = "FlashLoanReentrancy")]
fn test_prevents_same_block_withdrawal() {
let env = Env::default();
env.mock_all_auths();
let contract_id = env.register(FlashLoanGuardContract, ());
let client = FlashLoanGuardContractClient::new(&env, &contract_id);
let user = Address::generate(&env);

env.ledger().set_sequence_number(100);
client.deposit(&user);

// Attempt withdrawal in the same block (100) -> should panic
client.withdraw(&user);
}

#[test]
fn test_allows_subsequent_block_withdrawal() {
let env = Env::default();
env.mock_all_auths();
let contract_id = env.register(FlashLoanGuardContract, ());
let client = FlashLoanGuardContractClient::new(&env, &contract_id);
let user = Address::generate(&env);

env.ledger().set_sequence_number(100);
client.deposit(&user);

// Advance ledger block
env.ledger().set_sequence_number(101);
client.withdraw(&user); // Should succeed without panicking
}
}
45 changes: 45 additions & 0 deletions contracts/yield_vault/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use soroban_sdk::{contract, contractimpl, Address, Env};

@contract
pub struct YieldVaultContract;

@contractimpl
impl YieldVaultContract {
pub fn deposit(env: Env, user: Address, token_amount: i128, min_shares_out: i128) -> i128 {
user.require_auth();

// Calculate shares to mint based on current vault exchange rate
let shares_out = Self::calculate_shares_out(&env, token_amount);

if shares_out < min_shares_out {
panic!("SlippageExceeded: minted shares are less than min_shares_out");
}

// Perform deposit accounting and token transfer...
shares_out
}

pub fn withdraw(env: Env, user: Address, shares_in: i128, min_tokens_out: i128) -> i128 {
user.require_auth();

// Calculate tokens to return based on current vault exchange rate
let tokens_out = Self::calculate_tokens_out(&env, shares_in);

if tokens_out < min_tokens_out {
panic!("SlippageExceeded: returned tokens are less than min_tokens_out");
}

// Perform withdrawal accounting and token transfer...
tokens_out
}

fn calculate_shares_out(_env: &Env, token_amount: i128) -> i128 {
// Mock exchange rate logic: 1:1 ratio for demonstration
token_amount
}

fn calculate_tokens_out(_env: &Env, shares_in: i128) -> i128 {
// Mock exchange rate logic: 1:1 ratio for demonstration
shares_in
}
}
30 changes: 30 additions & 0 deletions contracts/yield_vault/src/test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#[cfg(test)]
mod test {
use super::*;
use soroban_sdk::{Env, Address};

#[test]
fn test_deposit_slippage_success() {
let env = Env::default();
env.mock_all_auths();
let contract_id = env.register(YieldVaultContract, ());
let client = YieldVaultContractClient::new(&env, &contract_id);
let user = Address::generate(&env);

let shares = client.deposit(&user, &1000, &950);
assert_eq!(shares, 1000);
}

#[test]
#[should_panic(expected = "SlippageExceeded")]
fn test_deposit_slippage_revert() {
let env = Env::default();
env.mock_all_auths();
let contract_id = env.register(YieldVaultContract, ());
let client = YieldVaultContractClient::new(&env, &contract_id);
let user = Address::generate(&env);

// Expecting 1050 shares out when deposit yields 1000 -> should panic
client.deposit(&user, &1000, &1050);
}
}