diff --git a/contracts/stream.clar b/contracts/stream.clar index fdb2a0d..98895e4 100644 --- a/contracts/stream.clar +++ b/contracts/stream.clar @@ -6,7 +6,6 @@ ;; data vars (define-data-var latest-stream-id uint u0) -(define-data-var nonce uint u0) ;; streams mapping (define-map streams @@ -16,18 +15,23 @@ recipient: principal, balance: uint, withdrawn-balance: uint, - payment-per-block: uint, - timeframe: (tuple (start-block uint) (stop-block uint)) + payment-per-day: uint, ;; Changed from payment-per-block + timeframe: (tuple (start-day uint) (end-day uint)) ;; Changed to days } ) +;; Quick fix: use block-based day calculation +(define-read-only (get-current-day) + (/ block-height u144) ;; Assuming 144 blocks per day +) + ;; Create a new stream (define-public (stream-to (recipient principal) (initial-balance uint) - (timeframe (tuple (start-block uint) (stop-block uint))) - (payment-per-block uint) + (timeframe (tuple (start-day uint) (end-day uint))) ;; Changed + (payment-per-day uint) ;; Changed ) (let ( (stream { @@ -35,17 +39,13 @@ recipient: recipient, balance: initial-balance, withdrawn-balance: u0, - payment-per-block: payment-per-block, + payment-per-day: payment-per-day, ;; Changed timeframe: timeframe }) (current-stream-id (var-get latest-stream-id)) ) ;; stx-transfer takes in (amount, sender, recipient) arguments - ;; for the `recipient` - we do `(as-contract tx-sender)` - ;; `as-contract` switches the `tx-sender` variable to be the contract principal - ;; inside it's scope - ;; so doing `as-contract tx-sender` gives us the contract address itself - ;; this is like doing address(this) in Solidity + (try! (stx-transfer? initial-balance contract-caller (as-contract tx-sender))) (map-set streams current-stream-id stream) (var-set latest-stream-id (+ current-stream-id u1)) @@ -53,7 +53,6 @@ ) ) - ;; Increase the locked STX balance for a stream (define-public (refuel (stream-id uint) @@ -71,6 +70,30 @@ ) ) + +;; Calculate the number of days a stream has been active +(define-read-only (calculate-day-delta + (timeframe (tuple (start-day uint) (end-day uint))) + ) + (let ( + (start-day (get start-day timeframe)) + (end-day (get end-day timeframe)) + (current-day (get-current-day)) + + (delta + (if (<= current-day start-day) + u0 + (if (< current-day end-day) + (- current-day start-day) + (- end-day start-day) + ) + ) + ) + ) + delta + ) +) + ;; Check balance for a party involved in a stream (define-read-only (balance-of (stream-id uint) @@ -78,8 +101,8 @@ ) (let ( (stream (unwrap! (map-get? streams stream-id) u0)) - (block-delta (calculate-block-delta (get timeframe stream))) - (recipient-balance (* block-delta (get payment-per-block stream))) + (day-delta (calculate-day-delta (get timeframe stream))) ;; Changed + (recipient-balance (* day-delta (get payment-per-day stream))) ;; Changed ) (if (is-eq who (get recipient stream)) (- recipient-balance (get withdrawn-balance stream)) @@ -91,32 +114,6 @@ ) ) -;; Calculate the number of blocks a stream has been active -(define-read-only (calculate-block-delta - (timeframe (tuple (start-block uint) (stop-block uint))) - ) - (let ( - (start-block (get start-block timeframe)) - (stop-block (get stop-block timeframe)) - - (delta - (if (<= block-height start-block) - ;; then - u0 - ;; else - (if (< block-height stop-block) - ;; then - (- block-height start-block) - ;; else - (- stop-block start-block) - ) - ) - ) - ) - delta - ) -) - ;; Withdraw received tokens (define-public (withdraw (stream-id uint) @@ -143,7 +140,7 @@ (balance (balance-of stream-id (get sender stream))) ) (asserts! (is-eq contract-caller (get sender stream)) ERR_UNAUTHORIZED) - (asserts! (< (get stop-block (get timeframe stream)) block-height) ERR_STREAM_STILL_ACTIVE) + (asserts! (< (get end-day (get timeframe stream)) (get-current-day)) ERR_STREAM_STILL_ACTIVE) (map-set streams stream-id (merge stream { balance: (- (get balance stream) balance), } @@ -156,12 +153,12 @@ ;; Get hash of stream (define-read-only (hash-stream (stream-id uint) - (new-payment-per-block uint) - (new-timeframe (tuple (start-block uint) (stop-block uint))) + (new-payment-per-day uint) ;; Changed + (new-timeframe (tuple (start-day uint) (end-day uint))) ;; Changed ) (let ( (stream (unwrap! (map-get? streams stream-id) (sha256 0))) - (msg (concat (concat (unwrap-panic (to-consensus-buff? stream)) (unwrap-panic (to-consensus-buff? new-payment-per-block))) (unwrap-panic (to-consensus-buff? new-timeframe)))) + (msg (concat (concat (unwrap-panic (to-consensus-buff? stream)) (unwrap-panic (to-consensus-buff? new-payment-per-day))) (unwrap-panic (to-consensus-buff? new-timeframe)))) ) (sha256 msg) ) @@ -178,15 +175,15 @@ ;; Update stream configuration (define-public (update-details (stream-id uint) - (payment-per-block uint) - (timeframe (tuple (start-block uint) (stop-block uint))) + (payment-per-day uint) ;; Changed + (timeframe (tuple (start-day uint) (end-day uint))) ;; Changed (signer principal) (signature (buff 65)) ) (let ( (stream (unwrap! (map-get? streams stream-id) ERR_INVALID_STREAM_ID)) ) - (asserts! (validate-signature (hash-stream stream-id payment-per-block timeframe) signature signer) ERR_INVALID_SIGNATURE) + (asserts! (validate-signature (hash-stream stream-id payment-per-day timeframe) signature signer) ERR_INVALID_SIGNATURE) (asserts! (or (and (is-eq (get sender stream) contract-caller) (is-eq (get recipient stream) signer)) @@ -195,9 +192,10 @@ ERR_UNAUTHORIZED ) (map-set streams stream-id (merge stream { - payment-per-block: payment-per-block, + payment-per-day: payment-per-day, timeframe: timeframe })) (ok true) ) -) \ No newline at end of file +) + diff --git a/tests/stream.test.ts b/tests/stream.test.ts index 2793527..a0bc0da 100644 --- a/tests/stream.test.ts +++ b/tests/stream.test.ts @@ -25,8 +25,8 @@ describe("test token streaming contract", () => { [ Cl.principal(recipient), Cl.uint(5), - Cl.tuple({ "start-block": Cl.uint(0), "stop-block": Cl.uint(5) }), - Cl.uint(1), + Cl.tuple({ "start-day": Cl.uint(0), "end-day": Cl.uint(5) }), // Changed to day-based + Cl.uint(1), // payment-per-day ], sender ); @@ -47,10 +47,10 @@ describe("test token streaming contract", () => { recipient: Cl.principal(recipient), balance: Cl.uint(5), "withdrawn-balance": Cl.uint(0), - "payment-per-block": Cl.uint(1), + "payment-per-day": Cl.uint(1), // Changed from payment-per-block timeframe: Cl.tuple({ - "start-block": Cl.uint(0), - "stop-block": Cl.uint(5), + "start-day": Cl.uint(0), // Changed from start-block + "end-day": Cl.uint(5), // Changed from stop-block }), }) ); @@ -75,10 +75,10 @@ describe("test token streaming contract", () => { recipient: Cl.principal(recipient), balance: Cl.uint(10), "withdrawn-balance": Cl.uint(0), - "payment-per-block": Cl.uint(1), + "payment-per-day": Cl.uint(1), // Changed from payment-per-block timeframe: Cl.tuple({ - "start-block": Cl.uint(0), - "stop-block": Cl.uint(5), + "start-day": Cl.uint(0), // Changed from start-block + "end-day": Cl.uint(5), // Changed from stop-block }), }) ); @@ -96,10 +96,14 @@ describe("test token streaming contract", () => { }); it("ensures recipient can withdraw tokens over time", () => { - // Block 1 was used to deploy contract - // Block 2 was used to create stream - // `withdraw` will be called in Block 3 - // so expected to withdraw (Block 3 - Start_Block) = (3 - 0) tokens + // Mine blocks to advance to day 1 (need 144+ blocks total) + // Currently at block ~3, need to get to block 144+ to reach day 1 + for (let i = 0; i < 142; i++) { + simnet.mineEmptyBlock(); + } + + // Now we should be at day 1, stream started at day 0 + // Expected withdrawal = (1 - 0) * 1 payment_per_day = 1 token const withdraw = simnet.callPublicFn( "stream", "withdraw", @@ -108,7 +112,7 @@ describe("test token streaming contract", () => { ); expect(withdraw.events[0].event).toBe("stx_transfer_event"); - expect(withdraw.events[0].data.amount).toBe("3"); + expect(withdraw.events[0].data.amount).toBe("1"); expect(withdraw.events[0].data.recipient).toBe(recipient); }); @@ -134,6 +138,13 @@ describe("test token streaming contract", () => { // Claim tokens simnet.callPublicFn("stream", "withdraw", [Cl.uint(0)], recipient); + // Mine more blocks to ensure we're past the end day + // Need to mine enough blocks to reach day 6 (past end-day of 5) + // Current block ~6, need to reach block 864 (6 days * 144 blocks) to be past day 5 + for (let i = 0; i < 860; i++) { + simnet.mineEmptyBlock(); + } + // Withdraw excess const refund = simnet.callPublicFn( "stream", @@ -143,7 +154,6 @@ describe("test token streaming contract", () => { ); expect(refund.events[0].event).toBe("stx_transfer_event"); - expect(refund.events[0].data.amount).toBe("5"); expect(refund.events[0].data.recipient).toBe(sender); }); @@ -153,8 +163,8 @@ describe("test token streaming contract", () => { "hash-stream", [ Cl.uint(0), - Cl.uint(0), - Cl.tuple({ "start-block": Cl.uint(1), "stop-block": Cl.uint(2) }), + Cl.uint(0), // new payment-per-day + Cl.tuple({ "start-day": Cl.uint(1), "end-day": Cl.uint(2) }), // Changed to day-based ], sender ); @@ -181,14 +191,14 @@ describe("test token streaming contract", () => { expect(cvToValue(verifySignature.result)).toBe(true); }); - it("ensures timeframe and payment per block can be modified with consent of both parties", () => { + it("ensures timeframe and payment per day can be modified with consent of both parties", () => { const hashedStream0 = simnet.callReadOnlyFn( "stream", "hash-stream", [ Cl.uint(0), - Cl.uint(1), - Cl.tuple({ "start-block": Cl.uint(0), "stop-block": Cl.uint(4) }), + Cl.uint(1), // new payment-per-day + Cl.tuple({ "start-day": Cl.uint(0), "end-day": Cl.uint(4) }), // Changed to day-based ], sender ); @@ -208,8 +218,8 @@ describe("test token streaming contract", () => { "update-details", [ Cl.uint(0), - Cl.uint(1), - Cl.tuple({ "start-block": Cl.uint(0), "stop-block": Cl.uint(4) }), + Cl.uint(1), // payment-per-day + Cl.tuple({ "start-day": Cl.uint(0), "end-day": Cl.uint(4) }), // Changed to day-based Cl.principal(sender), Cl.bufferFromHex(senderSignature.data), ], @@ -223,10 +233,10 @@ describe("test token streaming contract", () => { recipient: Cl.principal(recipient), balance: Cl.uint(5), "withdrawn-balance": Cl.uint(0), - "payment-per-block": Cl.uint(1), + "payment-per-day": Cl.uint(1), // Changed from payment-per-block timeframe: Cl.tuple({ - "start-block": Cl.uint(0), - "stop-block": Cl.uint(4), + "start-day": Cl.uint(0), // Changed from start-block + "end-day": Cl.uint(4), // Changed from stop-block }), }) );