KeeperHub / KeeperHub/keeperhub

Protocol read output overrides suggest a template path that resolves to undefined when the ABI output is unnamed

Closed
#2,514 0 comments 0 reactions 0 assignees View on GitHub
accepted bug confirmed
Dominant language
TypeScript
Stars
24
Forks
93
Avg merge
1d 8h
Merged PRs (30d)
266

Description

Found while reviewing #2506, which adds a read action whose ABI output is unnamed - the shape this bites.

**Reason.** `lib/protocol-registry.ts:492-495` surfaces a read action's declared `outputs` as template suggestions using the output name as a **top-level** field:

```ts
if (action.type === "read" && action.outputs) {
for (const output of action.outputs) {
outputs.push({ field: output.name, description: output.label });
}
}
```

At runtime the value is shaped by `plugins/web3/steps/structure-abi-result.ts:94-98`, and for a single output it keys the object by the **ABI's** output name, falling back to the bare scalar when the ABI names nothing:

```ts
if (outputs.length === 1) {
const output = outputs[0];
const structured = structureAbiValue(outputValues[0], output);
const name = output.name?.trim();
return name ? { [name]: structured } : structured;
}
```

So when a protocol declares an `outputs` override on an action whose ABI output is unnamed, the builder suggests `{{steps.X.}}` while the value actually sits at `{{steps.X.result}}`. The suggested path resolves to undefined, silently - the workflow saves, runs, and reads empty.

Existing instances on staging: `oft-token`, `oft-shared-decimals` and `oft-approval-required` in `protocols/layerzero.ts`, and any action #2506 adds in the same shape. The generic read-contract path does not have this problem - `lib/workflow/editor/action-output-fields.ts:95` emits `result.`.

The comment directly above the code at `:488-491` already reasons about exactly this failure class for write actions, and excludes them for that reason. The unnamed-ABI-output read case was not considered.

**Scope.** `lib/protocol-registry.ts`'s `buildOutputFieldsFromAction`, and whatever the chosen path prefix implies for existing declared overrides. Does not need any ABI to change - the on-chain ABIs are faithful as written, and diverging from them to paper over a template-path bug would be the wrong trade.

Out of scope: the multi-output branch, which keys by name or `unnamedOutput` and is self-consistent; and the write-action exclusion, which is correct as it stands.

**Plan.** Not settled, and the choice is worth a moment. Either emit `result.` to match the generic read path, which is consistent but wrong for the unnamed case where there is no `` at runtime; or have `structureAbiResult` key a single output by the declared override name when the ABI supplies none, so the suggestion and the value agree; or suppress the suggestion entirely when the ABI output is unnamed and let `result` stand on its own. The second is the only one where the user gets a named field, which is presumably why the override exists. A test asserting that every suggested template path resolves against the shape the step actually returns would stop the class rather than the instance.

Contributor guide

Open the contributing guide

Research direction

Start with buildOutputFieldsFromAction in lib/protocol-registry.ts:488-495 and compare its suggestions with structureAbiResult in plugins/web3/steps/structure-abi-result.ts:94-98. Review the generic read behavior in lib/workflow/editor/action-output-fields.ts:95 and the existing overrides in protocols/layerzero.ts. Done means the chosen behavior makes suggested template paths resolve against the returned shape, with a regression test covering an unnamed single ABI output.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
backend, developer-experience
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.