Skip to content

[A01-6] [LOW] FlowInitialized event payload not asserted #325

@thedavidmeister

Description

@thedavidmeister

Gap

FlowInitialized(address sender, EvaluableV2 evaluable) is emitted at src/concrete/Flow.sol:225 for each registered flow during flowInit. The existing tests use this event to extract the resulting evaluables (test/abstract/FlowTest.sol:74-79) but never assert:

  1. The sender field equals the expected msg.sender (the clone factory in the standard flow).
  2. The number of emitted events equals the number of evaluable configs.
  3. The emitted evaluable matches the deployer's returned (interpreter, store, expression).

Flow.construction.t.sol::testFlowConstructionInitialize only asserts the Initialize event payload; it ignores FlowInitialized entirely.

Source

  • src/concrete/Flow.sol:110 (event declaration)
  • src/concrete/Flow.sol:225 (emit)

Existing related tests

  • test/src/concrete/Flow.construction.t.sol::testFlowConstructionInitialize — asserts Initialize, not FlowInitialized.
  • test/src/concrete/Flow.expression.t.sol::testFlowBasicShouldDeployExpression — extracts evaluables but only checks expression field.

Proposed test

Add to test/src/concrete/Flow.construction.t.sol:

import {EvaluableV2} from "rain.interpreter.interface/lib/caller/LibEvaluable.sol";
import {LibLogHelper} from "test/lib/LibLogHelper.sol";

using LibLogHelper for Vm.Log[];

function testFlowConstructionEmitsFlowInitializedPerConfig(
    address[] memory expressions
) external {
    uint256 length = bound(expressions.length, 1, 5);
    assembly ("memory-safe") { mstore(expressions, length) }

    EvaluableConfigV3[] memory flowConfig = new EvaluableConfigV3[](length);
    for (uint256 i = 0; i < length; i++) {
        // Distinct deployer mock per expression so each evaluable is unique.
        expressionDeployerDeployExpression2MockCall(
            new bytes(0), new uint256[](0), expressions[i], bytes(hex"0007")
        );
        flowConfig[i] = EvaluableConfigV3(DEPLOYER, new bytes(0), new uint256[](0));
    }

    vm.recordLogs();
    address clone = I_CLONE_FACTORY.clone(deployFlowImplementation(), abi.encode(flowConfig));

    Vm.Log[] memory logs = vm.getRecordedLogs();
    Vm.Log[] memory initLogs = logs.findEvents(keccak256("FlowInitialized(address,(address,address,address))"));
    assertEq(initLogs.length, length, "wrong FlowInitialized count");

    for (uint256 i = 0; i < length; i++) {
        (address sender, EvaluableV2 memory evaluable) =
            abi.decode(initLogs[i].data, (address, EvaluableV2));
        assertEq(sender, address(I_CLONE_FACTORY), "wrong sender");
        assertEq(evaluable.expression, expressions[i], "wrong expression");
        assertEq(address(evaluable.interpreter), address(INTERPRETER), "wrong interpreter");
        assertEq(address(evaluable.store), address(STORE), "wrong store");
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    auditAudit findinglowSeverity: lowpass2Audit Pass 2: Test Coverage

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions