Skip to content

Initial pool deposit defines the resolution of shares #181

@Teolhyn

Description

@Teolhyn

The Attack Scenario

contracts/loan_pool/src/storage.rs:349-363:

pub fn get_shares_from_tokens(e: &Env, amount_tokens: i128) ->
Result<i128, LoanPoolError> {
    let shares = if total_pool_tokens == 0 {
        amount_tokens  // First deposit: 1:1 ratio
    } else {
        total_pool_shares * amount_tokens / total_pool_tokens  //
Integer division rounds down
    };
    Ok(shares)
}

Concrete Attack Example

  1. Alice deposits 10 stroops → Gets 10 shares
  • Pool: 10 tokens, 10 shares
  • 1 share = 1 token ✓
  1. Interest accrues over time, pool grows to 10,010 stroops
  • Pool: 10,010 tokens, 10 shares
  • 1 share = 1,001 tokens
  1. Bob deposits 2,000 stroops → (10 * 2,000) / 10,010 = 1.998... → Gets
    1 share (rounds down!)
  • Pool: 12,010 tokens, 11 shares
  • 1 share = 1,091.8 tokens
  1. Bob's loss:
  • Deposited: 2,000 stroops
  • Share value: 1,091 stroops
  • Lost: ~909 stroops (45.5%!)

Why This Happens

The resolution of shares is determined by the first deposit size. If that's tiny (10 stroops = 10 shares), each share represents a large unit, causing massive rounding errors as the pool grows. While it is unlikely for pool to accrue such large amount of tokens before more deposits flow in, this is unnecessary and easily avoidable attack vector. Here's some potential fixes:

Option 1: Minimum First Deposit

// In deposit() function
if current_contract_balance == 0 && amount < MINIMUM_INITIAL_DEPOSIT {
    return Err(LoanPoolError::DepositTooSmall);
}
const MINIMUM_INITIAL_DEPOSIT: i128 = 1_000_000; // 1 token with 7
decimals

Option 2: Virtual Shares/Tokens (Like Uniswap V2)

let shares = if total_pool_tokens == 0 {
    amount - MINIMUM_LIQUIDITY // Burn some shares initially
} else {
    // ... normal calculation
};

Option 3: Admin Initialization

  • Have the deployment script automatically seed each pool with a
    reasonable amount (e.g., 100-1000 tokens)
  • Located in scripts/initialize.ts (mentioned in package.json)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions