Skip to content

fix: classify workflow value fee from definition, not runtime gas#508

Merged
chrisli30 merged 2 commits into
stagingfrom
fix/value-fee-simulation-classification
Apr 8, 2026
Merged

fix: classify workflow value fee from definition, not runtime gas#508
chrisli30 merged 2 commits into
stagingfrom
fix/value-fee-simulation-classification

Conversation

@will-dz
Copy link
Copy Markdown
Collaborator

@will-dz will-dz commented Apr 8, 2026

buildValueFee was inspecting ExecutionLogs for CONTRACT_WRITE/ETH_TRANSFER step types or a LOOP step with non-zero TotalGasCost. Under Tenderly-backed simulation, a loop running an inner contractWrite records no real gas, so the same workflow was classified as Tier 1 in production but "no on-chain execution nodes" in simulation — causing the value fee to silently drop to 0% in simulate responses.

Classify off the workflow definition (task.Nodes) instead, mirroring classifyWorkflowValue. Loops are inspected for an inner ETHTransfer or ContractWrite runner. Simulation and production now agree.

Tests:

  • TestBuildValueFee_ClassifiesByDefinitionNotGas: direct unit coverage of buildValueFee across loop(contractWrite), pure off-chain, and direct contractWrite shapes.
  • TestSimulateTask_ValueFeeMatchesWorkflowDefinition: engine-level test exercising SimulateTask end-to-end with the revenue-splitter shape that triggered the bug, asserting Tier 1 / 0.03%.
  • TestSimulateTask_ValueFeeOffChainOnly: negative case asserting UNSPECIFIED / 0% for an off-chain-only workflow.

buildValueFee was inspecting ExecutionLogs for CONTRACT_WRITE/ETH_TRANSFER
step types or a LOOP step with non-zero TotalGasCost. Under Tenderly-backed
simulation, a loop running an inner contractWrite records no real gas, so
the same workflow was classified as Tier 1 in production but
"no on-chain execution nodes" in simulation — causing the value fee to
silently drop to 0% in simulate responses.

Classify off the workflow definition (task.Nodes) instead, mirroring
classifyWorkflowValue. Loops are inspected for an inner ETHTransfer or
ContractWrite runner. Simulation and production now agree.

Tests:
- TestBuildValueFee_ClassifiesByDefinitionNotGas: direct unit coverage of
  buildValueFee across loop(contractWrite), pure off-chain, and direct
  contractWrite shapes.
- TestSimulateTask_ValueFeeMatchesWorkflowDefinition: engine-level test
  exercising SimulateTask end-to-end with the revenue-splitter shape that
  triggered the bug, asserting Tier 1 / 0.03%.
- TestSimulateTask_ValueFeeOffChainOnly: negative case asserting
  UNSPECIFIED / 0% for an off-chain-only workflow.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a value-fee classification mismatch between production executions and Tenderly-backed simulations by classifying “on-chain-ness” from the workflow definition (task.Nodes) rather than runtime gas/logs.

Changes:

  • Update buildValueFee to detect on-chain execution via TaskNode/LoopNode runner types (ContractWrite/EthTransfer) instead of ExecutionLogs gas.
  • Wire the new buildValueFee(task.Nodes, ...) signature into both execution (RunTask) and simulation (SimulateTask) paths.
  • Add unit + engine-level regression tests covering loop(contractWrite) and off-chain-only workflows.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
core/taskengine/value_fee_simulation_test.go Adds end-to-end SimulateTask regression coverage to ensure simulation value fees match workflow definition.
core/taskengine/fee_estimator.go Switches buildValueFee classification from runtime steps/gas to workflow nodes (including loop runner inspection).
core/taskengine/fee_estimator_test.go Adds unit test coverage for buildValueFee classification based on definition.
core/taskengine/executor.go Updates execution path to call buildValueFee(task.Nodes, ...).
core/taskengine/engine.go Updates simulation path to call buildValueFee(task.Nodes, ...).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread core/taskengine/fee_estimator.go Outdated
Comment on lines +571 to +580
hasOnChainSteps := false
for _, step := range steps {
stepType := step.Type
if stepType == avsproto.NodeType_NODE_TYPE_CONTRACT_WRITE.String() ||
stepType == avsproto.NodeType_NODE_TYPE_ETH_TRANSFER.String() {
for _, node := range nodes {
switch {
case node.GetEthTransfer() != nil, node.GetContractWrite() != nil:
hasOnChainSteps = true
break
case node.GetLoop() != nil:
loop := node.GetLoop()
if loop.GetEthTransfer() != nil || loop.GetContractWrite() != nil {
hasOnChainSteps = true
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildValueFee now repeats the same on-chain-node detection logic as FeeEstimator.classifyWorkflowValue (fee_estimator.go:460-475). To avoid future drift between EstimateFees and execution/simulation responses, consider extracting a shared helper (e.g., hasOnChainNodes(nodes)) and using it in both places.

Copilot uses AI. Check for mistakes.
Comment thread core/taskengine/fee_estimator.go Outdated
func buildValueFee(nodes []*avsproto.TaskNode, feeRatesConfig *config.FeeRatesConfig) *avsproto.ValueFee {
rates := convertFeeRatesConfig(feeRatesConfig)

hasOnChainSteps := false
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable name hasOnChainSteps is a bit misleading now that this function inspects workflow nodes (not execution steps). Renaming to hasOnChainNodes would better reflect what’s being evaluated (and matches naming used in classifyWorkflowValue).

Copilot uses AI. Check for mistakes.
Addresses Copilot review feedback on PR #508:
- Both classifyWorkflowValue and buildValueFee duplicated the same
  on-chain detection loop, risking future drift between EstimateFees
  and simulation/execution responses. Extracted shared
  hasOnChainNodes(nodes) helper used by both call sites.
- The misleading hasOnChainSteps local in buildValueFee is gone with
  the loop, removing the steps-vs-nodes naming confusion.
@chrisli30 chrisli30 merged commit db8c735 into staging Apr 8, 2026
4 checks passed
@chrisli30 chrisli30 deleted the fix/value-fee-simulation-classification branch April 8, 2026 06:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants