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
- Read
contracts/ContributionGuide.md for the contribution workflow.
- Comment on the issue to claim it before starting.
- Branch:
fix/issue-119-insurance-zero-premium-rounding.
- Run
cargo fmt, cargo clippy -- -D warnings, cargo test before opening your PR.
- 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.
Description
purchase_policycomputes the premium ascoverage_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:Because integer division truncates, any
coverage_amountsuch thatcoverage_amount * 50 / 10_000 == 0— i.e.coverage_amount <= 199— producespremium == 0, and theif premium > 0guard skips the token transfer entirely. The function still proceeds to create and store a fullyactiveInsurancePolicywith the requestedcoverage_amount(up to 199) andpremium_paid: 0. The only guard oncoverage_amountiscoverage_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
This only rejects non-positive coverage; it does not enforce a minimum premium.
Fix direction
Either enforce a minimum
coverage_amountlarge enough that the premium can never round to zero at the configured rate (e.g.coverage_amount >= BASIS_POINTS_SCALE / DEFAULT_INSURANCE_PREMIUM_RATE_BPSrounded up), or explicitly reject the purchase when the computedpremium == 0instead of silently proceeding with free coverage.Acceptance Criteria
purchase_policycannot create anactivepolicy withpremium_paid == 0for a positivecoverage_amount.coverage_amountsmall enough to round the premium to zero under the current rate and confirms it is rejected (or a minimum premium is charged).test_premium_calculated_correctlyand other insurance tests still pass.cargo testpasses.Estimated Effort
Beginner: 3 hours
Intermediate: 1.5 hours
Expert: 1 hour
How to work this issue
contracts/ContributionGuide.mdfor the contribution workflow.fix/issue-119-insurance-zero-premium-rounding.cargo fmt,cargo clippy -- -D warnings,cargo testbefore opening your PR.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.