src/utils.ts:104-121 — withdrawableLocal computes effectiveNow as pause first, then end-time clamp:
const effectiveNow = stream.paused
? stream.pausedAt
: stream.endTime > 0 && nowSec > stream.endTime ? stream.endTime : nowSec;
The on-chain math::streamed_amount (contracts/stream/src/math.rs) clamps to end_time before checking is_paused(), so once now > end_time a still-paused stream reports the full amount on-chain but the frozen-at-pausedAt amount locally. (See conduit-protocol/streamFi-contracts issue on the same ordering.)
Compounding this: per the parseStreamInfo flags bug, stream.paused is always false here anyway, so the pause branch is currently dead.
Impact
withdrawableLocal (used for optimistic UI balances without a contract call) can under-report by the full paused interval versus what withdraw() will actually release.
Suggested fix
Match whichever ordering the contract settles on, and fix parseStreamInfo so stream.paused is real.
src/utils.ts:104-121—withdrawableLocalcomputeseffectiveNowas pause first, then end-time clamp:The on-chain
math::streamed_amount(contracts/stream/src/math.rs) clamps toend_timebefore checkingis_paused(), so oncenow > end_timea still-paused stream reports the full amount on-chain but the frozen-at-pausedAtamount locally. (See conduit-protocol/streamFi-contracts issue on the same ordering.)Compounding this: per the
parseStreamInfoflags bug,stream.pausedis alwaysfalsehere anyway, so the pause branch is currently dead.Impact
withdrawableLocal(used for optimistic UI balances without a contract call) can under-report by the full paused interval versus whatwithdraw()will actually release.Suggested fix
Match whichever ordering the contract settles on, and fix
parseStreamInfosostream.pausedis real.