KeeperHub / KeeperHub/keeperhub
Protocol read output overrides suggest a template path that resolves to undefined when the ABI output is unnamed
- 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
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