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
9 changes: 6 additions & 3 deletions contracts/finance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,9 +235,12 @@ To author a new curve, follow the `vesting_wallet_linear` pattern:
the witness-gated `consume_receipt` is what lets the curve run teardown logic or veto.

The curve **must be monotonically non-decreasing in time and bounded above by
`balance + released`.** A curve that violates either makes `release` abort before any
state changes - funds stay safe, but the release path is bricked until the curve is
fixed.
`balance + released`** - the curve module's responsibility, not something the primitive
enforces. `release` aborts (before any state change) only when the attested cumulative
dips below what is already released (`EVestedBelowReleased`) or exceeds `balance +
released` (`EInsufficientBalance`); an in-range regression does not abort, it just pays
the smaller increment. Funds stay safe either way (no over-release, no clawback), but a
careless curve can under-pay or brick the release path.

### The `VestedAmount` attestation

Expand Down
9 changes: 6 additions & 3 deletions contracts/finance/sources/vesting_wallet.move
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,12 @@
/// beneficiary and parameters for the curve module to destructure.
///
/// The curve must be monotonically non-decreasing in time and bounded above by
/// `balance + released`; violating either makes `release` abort before any state
/// mutation (funds stay safe, but the release path is bricked until the curve is
/// fixed).
/// `balance + released` - the curve module's responsibility, not something the primitive
/// enforces. `release` aborts (before any state mutation) only when the attested
/// cumulative dips below what is already released (`EVestedBelowReleased`) or exceeds
/// `balance + released` (`EInsufficientBalance`); an in-range regression does not abort,
/// it just pays the smaller increment. Funds stay safe either way (no over-release, no
/// clawback), but a careless curve can under-pay or brick the release path.
///
/// # Topologies
///
Expand Down
2 changes: 1 addition & 1 deletion contracts/finance/sources/vesting_wallet_linear.move
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
/// catch-up, then resumes its regular cadence.
/// - Mid-schedule: a staircase. With `k` full periods elapsed
/// (`k = (now - start_ms) / period_ms`, `0 <= k < steps`), the cumulative vested
/// total is `total * k / steps`, computed with a u128 intermediate. The value is
/// total is `total * k / steps`, computed with a u256 intermediate. The value is
/// flat across a period and steps up at each boundary.
/// - Post-end: clamped to the wallet's total (`balance + released`).
///
Expand Down
8 changes: 4 additions & 4 deletions contracts/finance/tests/vesting_wallet_linear_tests.move
Original file line number Diff line number Diff line change
Expand Up @@ -376,10 +376,10 @@ fun vested_amount_is_nondecreasing_in_time() {
test.end();
}

// The curve math uses a u128 intermediate, so the worst case
// The curve math uses a u256 intermediate, so the worst case
// (total = u64::MAX, last step before the end) does not overflow and fits in u64.
#[test]
fun vested_amount_uses_u128_intermediate_at_max() {
fun vested_amount_uses_u256_intermediate_at_max() {
let (mut test, mut clk) = setup(0);
let max = std::u64::max_value!();

Expand Down Expand Up @@ -828,10 +828,10 @@ fun vested_amount_continuous_is_linear_mid_schedule() {
test.end();
}

// The curve math uses a u128 intermediate, so the worst case
// The curve math uses a u256 intermediate, so the worst case
// (total = duration = u64::MAX) does not overflow and the final value fits in u64.
#[test]
fun vested_amount_continuous_uses_u128_intermediate_at_max() {
fun vested_amount_continuous_uses_u256_intermediate_at_max() {
let (mut test, mut clk) = setup(0);
let max = std::u64::max_value!();

Expand Down
Loading