Skip to content

Fix insurance::purchase_policy — Small Coverage Amounts Round the Premium Down to Zero, Granting Free Coverage #802

Description

@Samuel1505

Description

purchase_policy computes the premium as coverage_amount * DEFAULT_INSURANCE_PREMIUM_RATE_BPS / BASIS_POINTS_SCALE (0.5%, i.e. * 50 / 10_000) and only transfers a premium if it's positive:

let premium = coverage_amount
    .checked_mul(DEFAULT_INSURANCE_PREMIUM_RATE_BPS as i128)
    .ok_or(ContractError::InvalidInput)?
    .checked_div(BASIS_POINTS_SCALE as i128)
    .ok_or(ContractError::InvalidInput)?;

if premium > 0 {
    let token_client = token::Client::new(env, token);
    crate::reentrancy::protect_external_call(env, || {
        token_client.transfer(policyholder, env.current_contract_address(), &premium);
        Ok(())
    })?;
}

Because integer division truncates, any coverage_amount such that coverage_amount * 50 / 10_000 == 0 — i.e. coverage_amount <= 199 — produces premium == 0, and the if premium > 0 guard skips the token transfer entirely. The function still proceeds to create and store a fully active InsurancePolicy with the requested coverage_amount (up to 199) and premium_paid: 0. The only guard on coverage_amount is coverage_amount <= 0 (line 56), which does nothing to prevent this. A policyholder can therefore obtain genuine, claimable insurance coverage (up to 199 units of any token) without paying anything into the pool, funded entirely by other policyholders' premiums.

Technical Requirements

Files to update

  • contracts/contracts/stellar-grants/src/insurance.rs (purchase_policy, lines 46-114)

The gap

if coverage_amount <= 0 {
    return Err(ContractError::InvalidInput);
}

This only rejects non-positive coverage; it does not enforce a minimum premium.

Fix direction

Either enforce a minimum coverage_amount large enough that the premium can never round to zero at the configured rate (e.g. coverage_amount >= BASIS_POINTS_SCALE / DEFAULT_INSURANCE_PREMIUM_RATE_BPS rounded up), or explicitly reject the purchase when the computed premium == 0 instead of silently proceeding with free coverage.

Acceptance Criteria

  • purchase_policy cannot create an active policy with premium_paid == 0 for a positive coverage_amount.
  • A test attempts to purchase a policy with a coverage_amount small enough to round the premium to zero under the current rate and confirms it is rejected (or a minimum premium is charged).
  • Existing test_premium_calculated_correctly and other insurance tests still pass.
  • cargo test passes.

Estimated Effort

Beginner: 3 hours
Intermediate: 1.5 hours
Expert: 1 hour

How to work this issue

  1. Read contracts/ContributionGuide.md for the contribution workflow.
  2. Comment on the issue to claim it before starting.
  3. Branch: fix/issue-119-insurance-zero-premium-rounding.
  4. Run cargo fmt, cargo clippy -- -D warnings, cargo test before opening your PR.
  5. Use a Conventional Commit message, e.g. fix: prevent zero-premium insurance policies from rounding to free coverage.

Before you start

If you find this project interesting, please consider starring the repository on GitHub. It helps the project gain visibility and supports the Drips Wave program that rewards contributors for merged fixes like this one.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions