diff --git a/packages/nouns-contracts/contracts/StreamEscrow.sol b/packages/nouns-contracts/contracts/StreamEscrow.sol index e183e737d..99eef2fe8 100644 --- a/packages/nouns-contracts/contracts/StreamEscrow.sol +++ b/packages/nouns-contracts/contracts/StreamEscrow.sol @@ -253,19 +253,41 @@ 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; + return (0, 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); } }