From aa116ec54bda1521f3708d71129f4a16a1d27944 Mon Sep 17 00:00:00 2001 From: davidbrai Date: Thu, 12 Dec 2024 12:29:39 +0000 Subject: [PATCH 1/2] add view function for ticksLeft in stream --- .../contracts/StreamEscrow.sol | 33 ++++++++++++++++--- .../test/foundry/StreamEscrow.t.sol | 12 +++++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/packages/nouns-contracts/contracts/StreamEscrow.sol b/packages/nouns-contracts/contracts/StreamEscrow.sol index e183e737d..03d5f5252 100644 --- a/packages/nouns-contracts/contracts/StreamEscrow.sol +++ b/packages/nouns-contracts/contracts/StreamEscrow.sol @@ -253,19 +253,42 @@ contract StreamEscrow is IStreamEscrow { } /** - * @notice Returns the amount of ETH that was not yet streamed for a specific Noun token. + * @notice Returns the amount of ETH that was not yet streamed and the number of ticks left for a specific Noun token. * Returns zero for inactive streams. * @param nounId The ID of the Noun token to check the stream for. */ - function unstreamedETHForNoun(uint256 nounId) public view returns (uint256) { + function unstreamedETHAndTicksLeftForNoun( + uint256 nounId + ) public view returns (uint256 unstreamedETH, uint256 ticksLeft) { Stream memory stream = streams[nounId]; uint32 currentTick_ = currentTick; if (!isStreamActive(stream, currentTick_)) { - return 0; + ticksLeft = 0; + unstreamedETH = 0; + } else { + ticksLeft = stream.lastTick - currentTick_; + unstreamedETH = ticksLeft * stream.ethPerTick; } + } + + /** + * @notice Returns the amount of ETH that was not yet streamed for a specific Noun token. + * Returns zero for inactive streams. + * @param nounId The ID of the Noun token to check the stream for. + */ + function unstreamedETHForNoun(uint256 nounId) public view returns (uint256) { + (uint256 unstreamedETH, ) = unstreamedETHAndTicksLeftForNoun(nounId); + return unstreamedETH; + } - uint256 ticksLeft = stream.lastTick - currentTick_; - return ticksLeft * stream.ethPerTick; + /** + * @notice Returns the number of ticks left in a stream for a specific Noun token. + * Returns zero for inactive streams. + * @param nounId The ID of the Noun token to check the stream for. + */ + function ticksLeftForNoun(uint256 nounId) public view returns (uint256) { + (, uint256 ticksLeft) = unstreamedETHAndTicksLeftForNoun(nounId); + return ticksLeft; } function isStreamActive(Stream memory stream, uint32 tick) internal pure returns (bool) { diff --git a/packages/nouns-contracts/test/foundry/StreamEscrow.t.sol b/packages/nouns-contracts/test/foundry/StreamEscrow.t.sol index 99b755d65..c669393bd 100644 --- a/packages/nouns-contracts/test/foundry/StreamEscrow.t.sol +++ b/packages/nouns-contracts/test/foundry/StreamEscrow.t.sol @@ -735,6 +735,7 @@ contract UnstreamedETHTest is BaseStreamEscrowTest { // 1 ether / 20 = 0.05 eth per tick assertEq(escrow.unstreamedETHForNoun(1), 1 ether); + assertEq(escrow.ticksLeftForNoun(1), 20); // forward 5 ticks for (uint i; i < 5; i++) { @@ -742,6 +743,11 @@ contract UnstreamedETHTest is BaseStreamEscrowTest { } // check unstreamed eth assertEq(escrow.unstreamedETHForNoun(1), 0.75 ether); + assertEq(escrow.ticksLeftForNoun(1), 15); + + (uint256 unstreamedETH, uint256 ticksLeft) = escrow.unstreamedETHAndTicksLeftForNoun(1); + assertEq(unstreamedETH, 0.75 ether); + assertEq(ticksLeft, 15); // forward 15 more ticks for (uint i; i < 15; i++) { @@ -749,6 +755,7 @@ contract UnstreamedETHTest is BaseStreamEscrowTest { } // check unstreamed eth assertEq(escrow.unstreamedETHForNoun(1), 0 ether); + assertEq(escrow.ticksLeftForNoun(1), 0); } function test_unstreamedETHForNoun_canceledStream() public { @@ -757,6 +764,7 @@ contract UnstreamedETHTest is BaseStreamEscrowTest { // 1 ether / 20 = 0.05 eth per tick assertEq(escrow.unstreamedETHForNoun(1), 1 ether); + assertEq(escrow.ticksLeftForNoun(1), 20); // forward 5 ticks for (uint i; i < 5; i++) { @@ -764,6 +772,7 @@ contract UnstreamedETHTest is BaseStreamEscrowTest { } // check unstreamed eth assertEq(escrow.unstreamedETHForNoun(1), 0.75 ether); + assertEq(escrow.ticksLeftForNoun(1), 15); // cancel stream vm.prank(streamCreator); @@ -773,10 +782,12 @@ contract UnstreamedETHTest is BaseStreamEscrowTest { // check unstreamed eth is zero assertEq(escrow.unstreamedETHForNoun(1), 0 ether); + assertEq(escrow.ticksLeftForNoun(1), 0); } function test_unstreamedETHForNoun_returnsZeroForNonExistentStream() public { assertEq(escrow.unstreamedETHForNoun(1), 0 ether); + assertEq(escrow.ticksLeftForNoun(1), 0); // forward 5 ticks for (uint i; i < 5; i++) { @@ -784,6 +795,7 @@ contract UnstreamedETHTest is BaseStreamEscrowTest { } assertEq(escrow.unstreamedETHForNoun(3), 0 ether); + assertEq(escrow.ticksLeftForNoun(3), 0); } } From 00edb18295c65a5219134668400784787a22adba Mon Sep 17 00:00:00 2001 From: davidbrai Date: Thu, 12 Dec 2024 12:31:51 +0000 Subject: [PATCH 2/2] cleanup --- packages/nouns-contracts/contracts/StreamEscrow.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/nouns-contracts/contracts/StreamEscrow.sol b/packages/nouns-contracts/contracts/StreamEscrow.sol index 03d5f5252..99eef2fe8 100644 --- a/packages/nouns-contracts/contracts/StreamEscrow.sol +++ b/packages/nouns-contracts/contracts/StreamEscrow.sol @@ -263,8 +263,7 @@ contract StreamEscrow is IStreamEscrow { Stream memory stream = streams[nounId]; uint32 currentTick_ = currentTick; if (!isStreamActive(stream, currentTick_)) { - ticksLeft = 0; - unstreamedETH = 0; + return (0, 0); } else { ticksLeft = stream.lastTick - currentTick_; unstreamedETH = ticksLeft * stream.ethPerTick;