Skip to content

Migrate flow to V4 interpreter (eval4) + soldeer + reusable workflows #474

@thedavidmeister

Description

@thedavidmeister

Goal

Bring flow onto the current rain stack so it tracks latest and can use the upstream reusable CI/deploy workflows: migrate the interpreter integration V2 → V4 (eval4), de-submodule to soldeer, and convert workflows to the rainix reusables. flow is currently the org's straggler — submodules, OZ 4.x, the deprecated V2 interpreter API, and a vestigial deploy (manual-sol-artifacts.yaml runs rainix-sol-artifacts, which requires a script/Deploy.sol flow doesn't have → it would fail on dispatch).

Why now

  • The dead magic-nix-cache already breaks flow's deploy workflow; the proper fix is the reusable, which needs soldeer + a real Deploy.sol.
  • flow is pinned to a removed/deprecated interpreter API: LibEncodedDispatch no longer exists in rain.interpreter.interface, and the flat V2 interfaces (IInterpreterV2/StoreV2, IExpressionDeployerV3, IInterpreterCallerV2) are now under deprecated/. flow on V2 may already be incompatible with current deployed interpreters.

Upstream reference (security semantics — DO NOT invent)

Follow RaindexV6 / LibRaindex (the money-moving reference already on V4). The V4 model is eval-time, no deploy-time integrity check (the IExpressionDeployer model is gone):

(StackItem[] memory stack, bytes32[] memory writes) = evaluable.interpreter.eval4(
    EvalV4({
        store: evaluable.store,
        namespace: DEFAULT_STATE_NAMESPACE.qualifyNamespace(address(this)),
        bytecode: evaluable.bytecode,           // raw bytecode, not a deployed expression address
        sourceIndex: SourceIndexV2.wrap(FLOW_ENTRYPOINT),
        context: LibContext.build(context, signedContexts),  // bytes32[][]
        inputs: new StackItem[](0),
        stateOverlay: new bytes32[](0)
    })
);
if (writes.length > 0) evaluable.store.set(namespace, writes);

Work — interpreter V2 → V4

  1. Config EvaluableConfigV3{deployer,bytecode,constants}EvaluableV4{interpreter,store,bytecode} (no deployer/constants; bytecode carries constants).
  2. EvaluableV2{interpreter,store,expression}EvaluableV4; use V4 LibEvaluable hashing for registeredFlows.
  3. _flowStack: replace eval2(...encodedDispatch...) with the eval4(EvalV4{...}) block above.
  4. Stack/writes type uint256[]StackItem[](bytes32) + uint256[]bytes32[] — ripples through LibFlow.stackToFlow, the RAIN_FLOW_SENTINEL decode, and the transfer encoding. (Biggest piece.)
  5. Drop imports: LibEncodedDispatch, IExpressionDeployerV3, IInterpreterV2/StoreV2, IInterpreterCallerV2, EvaluableConfigV3. Add: IInterpreterV4, IInterpreterStoreV3, IInterpreterCallerV4 (EvaluableV4/EvalV4/SourceIndexV2), V4 LibContext/LibEvaluable, StackItem.
  6. IFlowV5IFlowV6 (breaking): flow() takes EvaluableV4; callerContext uint256[]bytes32[]; stackToFlow() takes StackItem[]. Bump dependent flow interfaces.
  7. Security semantics: drop the init-time deployExpression2 integrity check (UnsupportedFlowInputs/InsufficientFlowOutputs); enforce flow validity at eval-time per upstream (require stack.length >= MIN_FLOW_SENTINELS).
  8. Rewrite all ~10 test files (they build EvaluableConfigV3 + deploy expressions; Flow.time.t.sol's LibStackGeneration.generateFlowStack helper, etc.).

Work — de-submodule to soldeer (remappings-only, no src edits)

foundry.toml [dependencies] + [soldeer] recursive_deps=false + remappings; remove lib/ + .gitmodules. Deps: forge-std 1.16.1, OZ (5.6.1 to track latest — note OZ 4→5 is an extra code axis: security/utils/ for ReentrancyGuard, ECDSAUpgradeable removed, Multicall*; OR pin OZ 4.9.6 to defer), rain-factory 0.1.1, rain-interpreter-interface 0.1.0, rain-math-fixedpoint 0.2.0, rain-solmem 0.1.3 + transitive. Per-file remaps for the deprecated interfaces (interface/IInterpreterCallerV2.solinterface/deprecated/v1/..., IInterpreterStoreV2deprecated/v2/...).

Work — reusable workflows (the original ask)

  • rainix.yaml (CI) → rainlanguage/rainix/.github/workflows/rainix-sol.yaml (secrets: inherit). [its inline nix was already modernised in deploy: always sign with PRIVATE_KEY (never PRIVATE_KEY_DEV) #473]
  • Add script/Deploy.sol (deterministic Zoltu deploy of the Flow impl) + BuildPointers.sol + src/generated/Flow.pointers.sol. No legacy-redeploy concern (deploy was vestigial).
  • manual-sol-artifacts.yaml → reusable rainix-manual-sol-artifacts.yaml (DEPLOYMENT_SUITE).
  • Add git-clean.yaml → reusable rainix-copy-artifacts.yaml.

Notes

  • Breaking change to a financial contract → needs design review + heavy testing.
  • Will conflict with the ~22 open PRs (touches Flow.sol + tests) — sequence after the queue drains or coordinate rebases.
  • Scoping done 2026-06-06; local WIP foundry.toml/remappings were drafted in /tmp (not pushed).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions