From f04aa0c668f2091988e5279767704b29a6dbfc1d Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Wed, 6 May 2026 09:42:47 +0400 Subject: [PATCH 1/3] drop temporary comments on FlowUtilsAbstractTest wrappers The three wrappers (generateFlowStack, findEvent, findEvents) carried "A temporary solution for a smooth transition to using libraries" comments. They have persisted across multiple flow interface revisions and are best understood as permanent convenience wrappers that let inheriting test contracts call the helpers without re-importing the libraries or re-declaring `using` clauses. A NatSpec block on the contract states the new role; the misleading "temporary" markers are removed. Closes #413. Co-Authored-By: Claude Opus 4.7 (1M context) --- test/abstract/FlowUtilsAbstractTest.sol | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/abstract/FlowUtilsAbstractTest.sol b/test/abstract/FlowUtilsAbstractTest.sol index ec5a5467..3e95870c 100644 --- a/test/abstract/FlowUtilsAbstractTest.sol +++ b/test/abstract/FlowUtilsAbstractTest.sol @@ -10,21 +10,22 @@ import {LibStackGeneration} from "test/lib/LibStackGeneration.sol"; import {LibLogHelper} from "test/lib/LibLogHelper.sol"; import {FlowTransferOperation} from "test/abstract/FlowTransferOperation.sol"; +/// Convenience wrappers around `LibStackGeneration` and `LibLogHelper` so +/// test contracts inheriting this base can call `generateFlowStack` / +/// `findEvent` / `findEvents` directly without re-importing the libraries +/// or re-declaring `using` clauses. abstract contract FlowUtilsAbstractTest is FlowTransferOperation { using LibStackGeneration for uint256; using LibLogHelper for Vm.Log[]; - // A temporary solution for a smooth transition to using libraries. function generateFlowStack(FlowTransferV1 memory flowTransfer) internal pure returns (uint256[] memory stack) { stack = Sentinel.unwrap(RAIN_FLOW_SENTINEL).generateFlowStack(flowTransfer); } - // A temporary solution for a smooth transition to using libraries. function findEvent(Vm.Log[] memory logs, bytes32 eventSignature) internal pure returns (Vm.Log memory) { return logs.findEvent(eventSignature); } - // A temporary solution for a smooth transition to using libraries. function findEvents(Vm.Log[] memory logs, bytes32 eventSignature) internal pure From b71b175d21f2d304efe88ad5ead7b13d2cb0ac78 Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Sat, 9 May 2026 18:09:33 +0400 Subject: [PATCH 2/3] drop new NatSpec block on FlowUtilsAbstractTest The functions are pass-throughs and self-document; the WHY (`using` clauses don't inherit) is a Solidity invariant a reader of the file already knows. Co-Authored-By: Claude Opus 4.7 (1M context) --- test/abstract/FlowUtilsAbstractTest.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/abstract/FlowUtilsAbstractTest.sol b/test/abstract/FlowUtilsAbstractTest.sol index 3e95870c..7ab054ae 100644 --- a/test/abstract/FlowUtilsAbstractTest.sol +++ b/test/abstract/FlowUtilsAbstractTest.sol @@ -10,10 +10,6 @@ import {LibStackGeneration} from "test/lib/LibStackGeneration.sol"; import {LibLogHelper} from "test/lib/LibLogHelper.sol"; import {FlowTransferOperation} from "test/abstract/FlowTransferOperation.sol"; -/// Convenience wrappers around `LibStackGeneration` and `LibLogHelper` so -/// test contracts inheriting this base can call `generateFlowStack` / -/// `findEvent` / `findEvents` directly without re-importing the libraries -/// or re-declaring `using` clauses. abstract contract FlowUtilsAbstractTest is FlowTransferOperation { using LibStackGeneration for uint256; using LibLogHelper for Vm.Log[]; From 8894259b40bdcf87bf1e57dbe34126094cbca32a Mon Sep 17 00:00:00 2001 From: thedavidmeister Date: Sat, 9 May 2026 19:13:33 +0400 Subject: [PATCH 3/3] delete FlowUtilsAbstractTest, inline pass-through wrappers at callsites `generateFlowStack` / `findEvent` / `findEvents` were thin wrappers in `FlowUtilsAbstractTest`. The wrappers exist only because Solidity's `using X for T` clauses don't inherit, so child contracts of `FlowTest` couldn't otherwise call the library forms directly. Replace each callsite with the explicit static form: - `generateFlowStack(t)` -> `LibStackGeneration.generateFlowStack(Sentinel.unwrap(RAIN_FLOW_SENTINEL), t)` - `findEvent(logs, sig)` -> `LibLogHelper.findEvent(logs, sig)` `FlowTest` now inherits `FlowTransferOperation` directly. `FlowUtilsAbstractTest.sol` is removed. Co-Authored-By: Claude Opus 4.7 (1M context) --- test/abstract/FlowTest.sol | 4 +-- test/abstract/FlowUtilsAbstractTest.sol | 32 ---------------------- test/src/concrete/Flow.construction.t.sol | 3 +- test/src/concrete/Flow.context.t.sol | 7 +++-- test/src/concrete/Flow.expression.t.sol | 3 +- test/src/concrete/Flow.preview.t.sol | 3 +- test/src/concrete/Flow.signedContext.t.sol | 10 +++++-- test/src/concrete/Flow.time.t.sol | 7 +++-- test/src/concrete/Flow.transfer.t.sol | 24 ++++++++++------ 9 files changed, 41 insertions(+), 52 deletions(-) delete mode 100644 test/abstract/FlowUtilsAbstractTest.sol diff --git a/test/abstract/FlowTest.sol b/test/abstract/FlowTest.sol index 1c1d0186..1c31c52d 100644 --- a/test/abstract/FlowTest.sol +++ b/test/abstract/FlowTest.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.25; import {Vm} from "forge-std/Test.sol"; -import {FlowUtilsAbstractTest} from "test/abstract/FlowUtilsAbstractTest.sol"; +import {FlowTransferOperation} from "test/abstract/FlowTransferOperation.sol"; import {InterpreterMockTest} from "test/abstract/InterpreterMockTest.sol"; import {EvaluableConfigV3} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; @@ -16,7 +16,7 @@ import {Flow} from "src/concrete/Flow.sol"; import {LibUint256Matrix} from "rain.solmem/lib/LibUint256Matrix.sol"; import {LibUint256Array} from "rain.solmem/lib/LibUint256Array.sol"; -abstract contract FlowTest is FlowUtilsAbstractTest, InterpreterMockTest { +abstract contract FlowTest is FlowTransferOperation, InterpreterMockTest { using LibLogHelper for Vm.Log[]; using LibStackGeneration for uint256; using Address for address; diff --git a/test/abstract/FlowUtilsAbstractTest.sol b/test/abstract/FlowUtilsAbstractTest.sol deleted file mode 100644 index 7ab054ae..00000000 --- a/test/abstract/FlowUtilsAbstractTest.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: LicenseRef-DCL-1.0 -// SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd -pragma solidity ^0.8.25; - -import {Vm} from "forge-std/Test.sol"; - -import {FlowTransferV1, RAIN_FLOW_SENTINEL} from "src/interface/IFlowV5.sol"; -import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; -import {LibStackGeneration} from "test/lib/LibStackGeneration.sol"; -import {LibLogHelper} from "test/lib/LibLogHelper.sol"; -import {FlowTransferOperation} from "test/abstract/FlowTransferOperation.sol"; - -abstract contract FlowUtilsAbstractTest is FlowTransferOperation { - using LibStackGeneration for uint256; - using LibLogHelper for Vm.Log[]; - - function generateFlowStack(FlowTransferV1 memory flowTransfer) internal pure returns (uint256[] memory stack) { - stack = Sentinel.unwrap(RAIN_FLOW_SENTINEL).generateFlowStack(flowTransfer); - } - - function findEvent(Vm.Log[] memory logs, bytes32 eventSignature) internal pure returns (Vm.Log memory) { - return logs.findEvent(eventSignature); - } - - function findEvents(Vm.Log[] memory logs, bytes32 eventSignature) - internal - pure - returns (Vm.Log[] memory foundLogs) - { - foundLogs = logs.findEvents(eventSignature); - } -} diff --git a/test/src/concrete/Flow.construction.t.sol b/test/src/concrete/Flow.construction.t.sol index 03206941..78d22019 100644 --- a/test/src/concrete/Flow.construction.t.sol +++ b/test/src/concrete/Flow.construction.t.sol @@ -7,6 +7,7 @@ import {Vm} from "forge-std/Test.sol"; import {EvaluableConfigV3} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; import {FlowTest} from "test/abstract/FlowTest.sol"; import {EmptyFlowConfig} from "src/error/ErrFlow.sol"; +import {LibLogHelper} from "test/lib/LibLogHelper.sol"; contract FlowConstructionTest is FlowTest { function testFlowConstructionEmptyConfigReverts() external { @@ -30,7 +31,7 @@ contract FlowConstructionTest is FlowTest { Vm.Log[] memory logs = vm.getRecordedLogs(); bytes32 eventSignature = keccak256("Initialize(address,(address,bytes,uint256[])[])"); - Vm.Log memory concreteEvent = findEvent(logs, eventSignature); + Vm.Log memory concreteEvent = LibLogHelper.findEvent(logs, eventSignature); (address sender, EvaluableConfigV3[] memory config) = abi.decode(concreteEvent.data, (address, EvaluableConfigV3[])); diff --git a/test/src/concrete/Flow.context.t.sol b/test/src/concrete/Flow.context.t.sol index 6d30d27b..90615af4 100644 --- a/test/src/concrete/Flow.context.t.sol +++ b/test/src/concrete/Flow.context.t.sol @@ -3,12 +3,14 @@ pragma solidity =0.8.25; import {FlowTest} from "test/abstract/FlowTest.sol"; -import {IFlowV5} from "src/interface/IFlowV5.sol"; +import {IFlowV5, RAIN_FLOW_SENTINEL} from "src/interface/IFlowV5.sol"; +import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; import {FLOW_MAX_OUTPUTS, FLOW_ENTRYPOINT} from "src/concrete/Flow.sol"; import {LibEncodedDispatch} from "rain.interpreter.interface/lib/caller/LibEncodedDispatch.sol"; import {SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; import {LibContextWrapper} from "test/lib/LibContextWrapper.sol"; +import {LibStackGeneration} from "test/lib/LibStackGeneration.sol"; contract FlowContextTest is FlowTest { /** @@ -26,7 +28,8 @@ contract FlowContextTest is FlowTest { LibContextWrapper.buildAndSetContext(callerContext, signedContext, address(alice), address(flow)); { - uint256[] memory stack = generateFlowStack(transferEmpty()); + uint256[] memory stack = + LibStackGeneration.generateFlowStack(Sentinel.unwrap(RAIN_FLOW_SENTINEL), transferEmpty()); interpreterEval2MockCall(stack, new uint256[](0)); diff --git a/test/src/concrete/Flow.expression.t.sol b/test/src/concrete/Flow.expression.t.sol index 4ee547d0..b7970c79 100644 --- a/test/src/concrete/Flow.expression.t.sol +++ b/test/src/concrete/Flow.expression.t.sol @@ -12,6 +12,7 @@ import {LibUint256Matrix} from "rain.solmem/lib/LibUint256Matrix.sol"; import {LibContextWrapper} from "test/lib/LibContextWrapper.sol"; import {IInterpreterCallerV2} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; import {SignContextLib} from "test/lib/SignContextLib.sol"; +import {LibLogHelper} from "test/lib/LibLogHelper.sol"; contract FlowExpressionTest is FlowTest, IInterpreterCallerV2 { using SignContextLib for Vm; @@ -74,7 +75,7 @@ contract FlowExpressionTest is FlowTest, IInterpreterCallerV2 { ); Vm.Log[] memory logs = vm.getRecordedLogs(); - Vm.Log memory log = findEvent(logs, keccak256("Context(address,uint256[][])")); + Vm.Log memory log = LibLogHelper.findEvent(logs, keccak256("Context(address,uint256[][])")); (address sender, uint256[][] memory buildContextOutput) = abi.decode(log.data, (address, uint256[][])); assertEq(sender, address(this), "wrong sender"); diff --git a/test/src/concrete/Flow.preview.t.sol b/test/src/concrete/Flow.preview.t.sol index fd4d127d..bb086c86 100644 --- a/test/src/concrete/Flow.preview.t.sol +++ b/test/src/concrete/Flow.preview.t.sol @@ -14,6 +14,7 @@ import { } from "src/interface/IFlowV5.sol"; import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; import {LibEvaluable} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol"; +import {LibStackGeneration} from "test/lib/LibStackGeneration.sol"; contract FlowPreviewTest is FlowTest { using LibEvaluable for EvaluableV2; @@ -178,7 +179,7 @@ contract FlowPreviewTest is FlowTest { FlowTransferV1 memory flowTransfer = FlowTransferV1(new ERC20Transfer[](0), new ERC721Transfer[](0), new ERC1155Transfer[](0)); - uint256[] memory stack = generateFlowStack(flowTransfer); + uint256[] memory stack = LibStackGeneration.generateFlowStack(Sentinel.unwrap(RAIN_FLOW_SENTINEL), flowTransfer); assertEq( keccak256(abi.encode(flowTransfer)), keccak256(abi.encode(flow.stackToFlow(stack))), "wrong compare Structs" ); diff --git a/test/src/concrete/Flow.signedContext.t.sol b/test/src/concrete/Flow.signedContext.t.sol index beabbeae..2516aaf0 100644 --- a/test/src/concrete/Flow.signedContext.t.sol +++ b/test/src/concrete/Flow.signedContext.t.sol @@ -5,9 +5,11 @@ pragma solidity =0.8.25; import {Vm} from "forge-std/Test.sol"; import {FlowTest} from "test/abstract/FlowTest.sol"; import {SignContextLib} from "test/lib/SignContextLib.sol"; -import {IFlowV5} from "src/interface/IFlowV5.sol"; +import {IFlowV5, RAIN_FLOW_SENTINEL} from "src/interface/IFlowV5.sol"; +import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; import {EvaluableV2, SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; import {InvalidSignature} from "rain.interpreter.interface/lib/caller/LibContext.sol"; +import {LibStackGeneration} from "test/lib/LibStackGeneration.sol"; contract FlowSignedContextTest is FlowTest { using SignContextLib for Vm; @@ -32,7 +34,8 @@ contract FlowSignedContextTest is FlowTest { signedContexts[0] = vm.signContext(aliceKey, aliceKey, context0); signedContexts[1] = vm.signContext(aliceKey, aliceKey, context1); - uint256[] memory stack = generateFlowStack(transferEmpty()); + uint256[] memory stack = + LibStackGeneration.generateFlowStack(Sentinel.unwrap(RAIN_FLOW_SENTINEL), transferEmpty()); interpreterEval2MockCall(stack, new uint256[](0)); flow.flow(evaluable, new uint256[](0), signedContexts); @@ -63,7 +66,8 @@ contract FlowSignedContextTest is FlowTest { SignedContextV1[] memory signedContext = new SignedContextV1[](1); signedContext[0] = vm.signContext(aliceKey, aliceKey, context0); - uint256[] memory stack = generateFlowStack(transferEmpty()); + uint256[] memory stack = + LibStackGeneration.generateFlowStack(Sentinel.unwrap(RAIN_FLOW_SENTINEL), transferEmpty()); interpreterEval2MockCall(stack, new uint256[](0)); flow.flow(evaluable, new uint256[](0), signedContext); diff --git a/test/src/concrete/Flow.time.t.sol b/test/src/concrete/Flow.time.t.sol index 055cfa33..a3ac6519 100644 --- a/test/src/concrete/Flow.time.t.sol +++ b/test/src/concrete/Flow.time.t.sol @@ -3,10 +3,12 @@ pragma solidity =0.8.25; import {FlowTest} from "test/abstract/FlowTest.sol"; -import {IFlowV5} from "src/interface/IFlowV5.sol"; +import {IFlowV5, RAIN_FLOW_SENTINEL} from "src/interface/IFlowV5.sol"; +import {Sentinel} from "rain.solmem/lib/LibStackSentinel.sol"; import {EvaluableV2, SignedContextV1} from "rain.interpreter.interface/interface/IInterpreterCallerV2.sol"; import {DEFAULT_STATE_NAMESPACE} from "rain.interpreter.interface/interface/IInterpreterV2.sol"; import {IInterpreterStoreV2} from "rain.interpreter.interface/interface/IInterpreterStoreV2.sol"; +import {LibStackGeneration} from "test/lib/LibStackGeneration.sol"; contract FlowTimeTest is FlowTest { function testFlowBasicFlowTime(uint256[] memory writeToStore) public { @@ -14,7 +16,8 @@ contract FlowTimeTest is FlowTest { (IFlowV5 flow, EvaluableV2 memory evaluable) = deployFlow(); - uint256[] memory stack = generateFlowStack(transferEmpty()); + uint256[] memory stack = + LibStackGeneration.generateFlowStack(Sentinel.unwrap(RAIN_FLOW_SENTINEL), transferEmpty()); interpreterEval2MockCall(stack, writeToStore); diff --git a/test/src/concrete/Flow.transfer.t.sol b/test/src/concrete/Flow.transfer.t.sol index 50c6e581..a099ed94 100644 --- a/test/src/concrete/Flow.transfer.t.sol +++ b/test/src/concrete/Flow.transfer.t.sol @@ -186,8 +186,10 @@ contract FlowTransferTest is FlowTest { erc20Transfers[0] = ERC20Transfer({token: TOKEN_A, from: bob, to: address(flow), amount: erc20Amount}); erc20Transfers[1] = ERC20Transfer({token: TOKEN_B, from: address(flow), to: alice, amount: erc20Amount}); - uint256[] memory stack = - generateFlowStack(FlowTransferV1(erc20Transfers, new ERC721Transfer[](0), new ERC1155Transfer[](0))); + uint256[] memory stack = LibStackGeneration.generateFlowStack( + Sentinel.unwrap(RAIN_FLOW_SENTINEL), + FlowTransferV1(erc20Transfers, new ERC721Transfer[](0), new ERC1155Transfer[](0)) + ); interpreterEval2MockCall(stack, new uint256[](0)); } @@ -203,8 +205,10 @@ contract FlowTransferTest is FlowTest { erc20Transfers[1] = ERC20Transfer({token: TOKEN_B, from: bob, to: alice, amount: erc20Amount}); vm.mockCall(TOKEN_A, abi.encodeWithSelector(IERC20.transferFrom.selector), abi.encode(true)); - uint256[] memory stack = - generateFlowStack(FlowTransferV1(erc20Transfers, new ERC721Transfer[](0), new ERC1155Transfer[](0))); + uint256[] memory stack = LibStackGeneration.generateFlowStack( + Sentinel.unwrap(RAIN_FLOW_SENTINEL), + FlowTransferV1(erc20Transfers, new ERC721Transfer[](0), new ERC1155Transfer[](0)) + ); interpreterEval2MockCall(stack, new uint256[](0)); } @@ -236,8 +240,10 @@ contract FlowTransferTest is FlowTest { erc721Transfers[0] = ERC721Transfer({token: TOKEN_A, from: bob, to: address(flow), id: erc721TokenId}); erc721Transfers[1] = ERC721Transfer({token: TOKEN_B, from: address(flow), to: alice, id: erc721TokenId}); - uint256[] memory stack = - generateFlowStack(FlowTransferV1(new ERC20Transfer[](0), erc721Transfers, new ERC1155Transfer[](0))); + uint256[] memory stack = LibStackGeneration.generateFlowStack( + Sentinel.unwrap(RAIN_FLOW_SENTINEL), + FlowTransferV1(new ERC20Transfer[](0), erc721Transfers, new ERC1155Transfer[](0)) + ); interpreterEval2MockCall(stack, new uint256[](0)); } @@ -280,8 +286,10 @@ contract FlowTransferTest is FlowTest { token: TOKEN_B, from: address(flow), to: alice, id: erc1155InTokenId, amount: erc1155InAmount }); - uint256[] memory stack = - generateFlowStack(FlowTransferV1(new ERC20Transfer[](0), new ERC721Transfer[](0), erc1155Transfers)); + uint256[] memory stack = LibStackGeneration.generateFlowStack( + Sentinel.unwrap(RAIN_FLOW_SENTINEL), + FlowTransferV1(new ERC20Transfer[](0), new ERC721Transfer[](0), erc1155Transfers) + ); interpreterEval2MockCall(stack, new uint256[](0)); }