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
3 changes: 3 additions & 0 deletions contracts/stream-manager.clar
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@
(asserts! (not (var-get emergency-paused)) ERR-NOT-AUTHORIZED)
(asserts! (> deposit-amount u0) ERR-INVALID-AMOUNT)
(asserts! (> duration-blocks u0) ERR-INVALID-DURATION)
;; Integer rate = (deposit * PRECISION) / duration must be >= 1, otherwise
;; rate-per-block is 0 (no accrual) and top-up-stream divides by zero.
(asserts! (>= (* deposit-amount PRECISION) duration-blocks) ERR-INVALID-DURATION)
(asserts! (>= start-block stacks-block-height) ERR-INVALID-START-TIME)
(asserts! (not (is-eq recipient contract-caller)) ERR-INVALID-RECIPIENT)
(asserts! (not (is-eq recipient (as-contract tx-sender))) ERR-INVALID-RECIPIENT)
Expand Down
15 changes: 15 additions & 0 deletions tests/stream-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,21 @@ describe("StackStream - Stream Manager Contract", () => {
expect(result.result).toBeErr(Cl.uint(301)); // ERR-INVALID-DURATION
});

it("should fail when duration exceeds deposit * PRECISION (zero rate-per-block)", () => {
const startBlock = getCurrentBlock() + 1;
// 1 * PRECISION / (PRECISION + 1) rounds down to 0 — must be rejected
const precision = 1_000_000_000_000n;
const result = createStream(
wallet1,
wallet2,
1,
startBlock,
Number(precision + 1n)
);

expect(result.result).toBeErr(Cl.uint(301)); // ERR-INVALID-DURATION
});

it("should fail with start block in the past", () => {
// Mine some blocks first so we have room for a past block
simnet.mineEmptyBlocks(5);
Expand Down