Skip to content

feat(PT9-502): add chained postInteraction (tail) encode/decode - #178

Open
rharutyunyan wants to merge 8 commits into
mainfrom
feat/PT9-502-chained-postinteraction
Open

feat(PT9-502): add chained postInteraction (tail) encode/decode#178
rharutyunyan wants to merge 8 commits into
mainfrom
feat/PT9-502-chained-postinteraction

Conversation

@rharutyunyan

@rharutyunyan rharutyunyan commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

What

Adds Extra.chainedPostInteraction (string | string[]) to Fusion orders: the raw bytes are encoded after SurplusParams in the FeeTaker-style settlement postInteraction payload and decoded back on the way in.

This matches FeeTaker.sol's on-chain semantics: after flags, fee recipients, optional custom receiver, fee/whitelist data and surplus params are consumed, a remaining tail of >= 20 bytes is called as IPostInteraction(address(bytes20(tail))) with tail[20:] as extraData. Needed for Ether.fi postInteraction chaining in Fusion swaps (same pattern as the earlier OKX integration).

Details

  • Encode: string[] chunks are concatenated in order into a single tail; every chunk is validated as 0x-prefixed even-length hex before joining (odd-length chunks throw instead of silently merging into corrupted tail data). An empty concatenation ([]) means no tail.
  • Decode: target-only 20-byte tails are supported (FeeTaker calls the target for any tail of at least 20 bytes); non-hex or <20-byte decode leftovers throw invalid interaction instead of silently producing a broken round-trip. The target-only stand-in lives in src/fusion-order/target-only-interaction.ts (the Interaction constructor rejects empty data, so a dedicated class is needed).
  • Based on the reference implementation belactriple9/fusion-sdk@0f925cd (Caleb Rippey), with one intentional narrowing: makerPermit / preInteraction decoding keeps strict Interaction.decode — the lenient target-only path applies only to the chained tail (a target-only permit is invalid on-chain and upstream never accepted a 20-byte preInteraction).

CI fixes (separate commits)

  • Audit: the pr-check Security step (pnpm audit --audit-level high) fails on main's unchanged lockfile with 11 high advisories published after the last green run (Jul 7) — any PR against main hits this today. Remediated via the existing pnpm.overrides convention (axios >=1.18.0, brace-expansion per-major patched releases, js-yaml 3.15.0/4.3.0, fast-uri >=3.1.4). GHSA-mh99-v99m-4gvg (brace-expansion OOM, "patched" only in 5.0.8) is ignored via pnpm.auditConfig.ignoreGhsas because forcing 5.x onto the minimatch 3.x/9.x CJS consumers breaks eslint (expand is not a function); exposure is dev-tooling-only glob expansion of repo-controlled patterns. Remaining low/moderate advisories don't gate --audit-level high and are left untouched.
  • Publish workflow: publish.yml pinned version: 10 on pnpm/action-setup@v4, which conflicts with the packageManager: pnpm@10.26.2 field added to package.json on main in 1591f35 (after the last 2.4.x publish) — the action hard-errors with "Multiple versions of pnpm specified", so both publish jobs failed at the Install pnpm step for v2.4.11-rc.0 (run 30270683953). Dropped the input so packageManager is the single source of truth, matching pr-check.yml. Note: tags cut before this fix (e.g. the existing v2.4.11-rc.0) still carry the broken workflow and must be re-cut from a head that includes it.

Verification

  • pnpm lint:ci — 0 errors (19 pre-existing warnings in ws-api/connector)
  • pnpm lint:types — clean
  • pnpm test — 17 suites / 104 tests passed
  • pnpm build — esm + cjs + types clean
  • pnpm audit --audit-level high — exit 0

Downstream

1inch/gasless-swap.1inch.io#1684 activates its chained-postInteraction target allowlist once the @1inch/fusion-sdk-v2.2-surplus alias in gasless is bumped to a release that includes this change (until then, tailed orders are safely rejected at parse).

Reference

Jira: PT9-502

Open in Web Open in Cursor 

cursoragent and others added 3 commits July 27, 2026 12:13
Let Fusion orders carry an extra postInteraction appended after the
settlement's own FeeTaker-style data. Matches FeeTaker.sol semantics:
after flags, fee recipients, optional custom receiver, fee/whitelist
data and surplus params are consumed, a remaining tail of >= 20 bytes
is called as IPostInteraction(address(bytes20(tail))) with tail[20:].

Based on the reference implementation by Caleb Rippey (1inch Support):
belactriple9/fusion-sdk 0f925cd "Add chained post-interaction encoding".

Adaptations to the reference diff:
- (a) Narrowed the lenient target-only decode to the chained tail only.
  makerPermit/preInteraction keep strict Interaction.decode: a
  target-only permit is invalid on-chain and upstream never accepted a
  20-byte preInteraction, so narrowing keeps the upstream change
  minimal; all inherited specs still pass.
- (d) decodeInteraction rejects non-hex or <20-byte payloads with a
  clear assert. This validates raw chainedPostInteraction input at
  build time and makes a <20-byte decode leftover throw instead of
  silently producing a broken round-trip; an empty chunk concatenation
  ([]) is treated as no tail.
- Chunked string[] input is validated per chunk (each must be valid
  even-length hex bytes) so odd-length chunks cannot silently merge
  into corrupted tail data.
Advisories published after the last green CI run (Jul 7) fail the
pr-check Security step (pnpm audit --audit-level high) for any PR
against main, independent of this branch's changes. Remediate the
11 high findings via the existing pnpm.overrides convention:

- axios >=1.18.0 (proxy-after-interception advisory; extends the
  existing >=1.15.0 override)
- brace-expansion: per-major patched releases (1.1.16 / 2.1.2 /
  5.0.8) for the exponential-time DoS advisories
- js-yaml 3.15.0 / 4.3.0 (merge-key quadratic CPU)
- fast-uri >=3.1.4 (host confusion)

GHSA-mh99-v99m-4gvg (brace-expansion OOM) is ignored via
pnpm.auditConfig.ignoreGhsas: it marks every release below 5.0.8
vulnerable, but 5.x exports break the CJS consumers pinned to
minimatch 3.x/9.x (eslint fails with 'expand is not a function'
when forced to 5.0.8). Dev-tooling-only exposure: glob expansion
of repo-controlled patterns in eslint/@swc/cli.

Remaining low/moderate advisories (testcontainers tree) do not
gate --audit-level high and are left untouched.

Co-authored-by: Radik Harutyunyan <rharutyunyan@users.noreply.github.com>
Co-authored-by: Radik Harutyunyan <rharutyunyan@users.noreply.github.com>
@rharutyunyan
rharutyunyan marked this pull request as ready for review July 27, 2026 12:50
CI/CD Bot and others added 5 commits July 27, 2026 13:31
publish.yml pinned 'version: 10' on pnpm/action-setup@v4 while
package.json declares packageManager pnpm@10.26.2 (added on main in
1591f35, after the last 2.4.x publish). The action refuses the
conflicting specs, so both publish jobs fail at the Install pnpm step
('Multiple versions of pnpm specified', run 30270683953 for
v2.4.11-rc.0). Drop the input so the packageManager field is the
single source of truth, matching pr-check.yml.

Co-authored-by: Radik Harutyunyan <rharutyunyan@users.noreply.github.com>
Co-authored-by: Radik Harutyunyan <rharutyunyan@users.noreply.github.com>
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.

2 participants