Skip to content

chore(deps)!: update Fuel-owned dependencies and toolchains - #3977

Merged
pixelcircuits merged 12 commits into
masterfrom
pixelcircuits/build/fuel-owned-dependency-upgrades
Oct 27, 2026
Merged

pixelcircuits merged 12 commits into
masterfrom
pixelcircuits/build/fuel-owned-dependency-upgrades

Conversation

@pixelcircuits

@pixelcircuits pixelcircuits commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Consume the published VM ASM WASM initialization reuse fix and update the SDK's Fuel-owned toolchain and example/template dependencies. This includes the compiler and local-node migrations required for the updated versions to build and pass the SDK tests.

Packages/tool Before After
@fuels/vm-asm (account, contract, program, fuels, Next.js/Vite demos) 0.62.0 0.66.4
@fuels/react, @fuels/connectors (counter guide, wallet demo, Next.js/Vite templates) 0.39.2 0.45.0
Bundled fuel-core, schema, supported version, template toolchains 0.47.1 0.48.3
Bundled forc, forc-fmt/std, template toolchains 0.68.7 0.72.1
Documentation's native fuel-asm example 0.60.2 0.66.4
Documentation's native example Rust minimum 1.85 1.93

Refresh the Core schema while querying the existing script-parameter fields, keeping those queries compatible with older nodes. Decode ABI 1.2 and later panic indices/call-site bits alongside legacy error codes, retaining legacy fallback when version metadata is absent, selecting the ABI by the reverting program’s receipt ID. Preserve the primary contract’s identity across transaction/call results so contracts sharing the same panic index report their own message and source location. If the program’s ABI or panic entry is unavailable, retain the generic revert reason. Remove obsolete ASM type suppressions, compiler flags and duplicate standard-library implementations; regenerate ABI binding fixtures and update compiler/gas expectations.

The executor upgrade test derives its fixture from the installed Core WASM during pretest, including Git-branch builds, replacing a stale multi-megabyte inline literal. Its final upload subsection retains the exact bytes without padding. Template UI tests assert actual counter values, and the Next.js root layout renders the required HTML/body shell during SSR. Tests that inspect already-confirmed or reconstructed transaction responses explicitly wait for confirmation. The local-node launcher waits for a successful GraphQL health query after its port is bound, with bounded retries. Startup failures cancel readiness polling and attempt child-process cleanup while removing temporary snapshots and process handlers; early exits report their exit code and signal. Cleanup errors cannot prevent the original launch rejection. Failed launches let asynchronous CLI failure hooks finish; only unexpected exits after a successful launch propagate to the parent, and signal termination reports a nonzero status. Parent-process signals have separate handlers: the first Ctrl+C or restart signal terminates the CLI with its conventional signal status, while uncaught exceptions exit 1. Returned cleanup handles remain non-exiting. When a launcher exits, its exit handler attempts to stop any surviving processes in the detached group once, tolerates secondary signal errors, and records the stopped state. Repeated cleanup after exit does not retry the signal or throw a secondary teardown error. Disposal tests wait for graceful shutdown; block-height cache tests control block production; local message-proof examples explicitly produce their confirmation block.

Dependency checking generates the executor fixture before scanning imports. Both Core VERSION and the installed executor WASM bytes participate in Turbo’s global dependency hash, invalidating the fixture when a Git branch moves without changing its configured name. Git builds package the executor artifact produced by Cargo and fail clearly before replacing installed artifacts if the build fails or produces no WASM. CI's default test setup uses Node 20.20, matching the other CI setup action; the updated template dependency tree includes @solana/codecs-numbers requiring Node >=20.18, so the former Node 20.14 default cannot install it. The root, all published SDK packages, create-fuels and both templates now declare ^20.18.0 || ^22.0.0 || ^24.0.0 consistently with the repository manifest policy. The create-fuels guide, README and changeset document this requirement; .nvmrc selects the same Node 20.20.0 used by the default CI setup.

Coverage comparisons now use a baseline for the PR's exact base commit. CI restores that SHA's coverage cache or builds and tests the base to generate it, then downloads the baseline from the same workflow run. Master builds seed the shared cache; expired artifacts or caches cannot permanently break the check. Missing baseline generation and failed prerequisite tests still fail the required aggregate job. Documentation links now point to the maintained template source, generated API reference, BIP32 and RustCrypto's TAI64 reference.

Breaking Changes

The declared Node.js 20 minimum is now 20.18 across published SDK packages, create-fuels and generated templates. Node.js 22 and 24 remain supported.

The default local-node snapshot uses the V7 opcode gas-cost table shipped in the Core 0.48.3 binary, retaining the SDK’s existing fee parameters. SnapshotConfigs accepts V4 or V7; callers reading gas_costs.V4 directly must narrow the variant first. Explicit V4 snapshots remain supported, and partial V4 overrides are merged onto the SDK’s previous V4 defaults. Nested overrides and empty gas-cost overrides have live-node regression coverage. The changeset requests a minor release for this public type/default change.

Recompiling Sway with Forc 0.72.1 can change bytecode, gas usage, panic encoding and dynamic-bytes hashes; older source may require the equality/storage migrations demonstrated by the fixtures. Existing deployed bytecode is not recompiled by installing this SDK update.

Validation

Node 22.13.1 / pnpm 9.4.0, Forc 0.72.1, Core 0.48.3, Chromium:

  • Full SDK, documentation and both template builds pass; pretest and typegen golden tests pass.
  • Sway unit tests: all 16 pass across the counter guide and both templates.
  • Full Node suite at 557e55058: 2,816 tests pass, 3 skipped and 6 todo, across 553 passing files and one skipped file, with four workers. A 15-second test timeout accommodates this WSL environment's loopback refusal delay; repository concurrency and timeout settings are unchanged.
  • Browser: the previous full suite passed 1,831 tests, 2 skipped and 6 todo, across 389 files. The latest decoder follow-up passes all 20 targeted Chromium tests, including ABI 1.3/1.10 and versionless legacy ABIs. The preceding panic-attribution fix passed all 35 targeted Chromium tests across decoder, contract and live-node revert suites. Earlier cache, serialization and message-proof changes passed all 15 targeted browser tests across four files.
  • Vite and Next.js template burner-wallet top-up and counter transaction UI tests pass. Next.js validation waits for deployment before opening the app, avoiding a stale contract ID during simultaneous server startup.
  • Real wallet transfers and chain queries pass against both Core 0.47.3 and 0.48.3. This probe covers SDK queries/transfers; compiler-generated programs were tested against 0.48.3.
  • Review follow-up: five live gas-default/override regressions pass. Turbo dry-runs prove changing either Core VERSION or executor WASM bytes changes the pretest cache key. Four packaging regressions cover fresh/existing checkouts, moving branches, missing WASM and failed builds. Four readiness regressions cover stderr errors, process errors, exit codes and signals while a health request is pending; ABI regressions cover 1.3/1.10 alongside legacy 1.1 and packed 1.2.
  • Panic attribution: collision cases cover ABI 1.1 and 1.2, nonzero call-site bits, primary/external contracts and scripts. Unknown contracts, missing receipts and missing panic entries retain generic errors; older callers can identify the primary contract through its top-level call receipt. The initial regression selection failed nine cases before the fix and passes afterward.
  • Frozen lockfile install, dependency sync, dependency checking from a clean generated-fixture state, package manifests/exports, Forc formatting, lint and typechecks pass; the native Rust ASM documentation example builds.
  • Documentation/coverage CI follow-up at b422fd1cb: full Markdown link checking and lint/typechecks/formatting pass locally. The actual aggregate guard passes nine PR/master success and failure scenarios; the coverage report preserves positive/negative deltas and fails without a baseline. Fresh CI passed Lint, Node 20/22/24, 1,848 browser tests, Forc, E2E and the required aggregate test. Cold exact-base baseline generation passed 2,772 Node and 1,828 browser tests; the aggregate successfully downloaded its same-run artifact, generated the real coverage diff and published the report. A transient Ubuntu package-mirror timeout in the head browser job passed on targeted retry.
  • Claude independently reviewed the six-file documentation/coverage follow-up and found one artifact-name conflict on “Re-run all jobs”. Both coverage-master uploads now set overwrite: true in 26085796f. Claude verified the correction and explicitly agreed with the complete scoped change. Formatting/whitespace checks pass; actionlint has no new diagnostics versus the original workflow. Fresh CI for this two-line correction is pending.
  • Local-node lifecycle: all 66 focused tests, both real CLI regressions, 28 build tasks and full lint/typechecks pass. Simulated EPERM cases cover startup rejection and a single post-exit group-cleanup attempt without retries. A real wrapper-only SIGKILL regression verifies its Fuel Core descendant also stops. CLI subprocess tests verify asynchronous failure hooks finish before exit 1 and the first Ctrl+C exits 130. Claude reviewed the final change at d841902f0, independently repeated these focused/CLI checks and verified wrapper-descendant cleanup, with no remaining concerns. EPERM was simulated on Linux; no native macOS run is claimed.
  • Startup stress: 1,600 launches with 16 workers on two CPUs pass with zero resets; the prior launcher had 14 resets in the identical probe. A regression test also holds the health response to verify launch remains pending until the API can serve requests.
  • A probe making 1,000 concurrent and 1,000 sequential initialization requests allocates one WebAssembly module with ASM 0.66.4 versus 2,001 with 0.62.0.

This verifies initialization reuse; it does not establish that every O2/Stork memory leak is fixed. Downstream services benefit after consuming an SDK release. React/connectors 0.45.0 declares an exact fuels 0.102.0 peer while this workspace is 0.103.0; actual workspace builds and burner-wallet browser flows were tested. Node 20/22/24, browser, Forc, E2E, Lint and the aggregate test all passed on b422fd1cb. The subsequent two-line artifact-overwrite correction is pushed as 26085796f; its fresh CI results are pending. Other installed-wallet providers and Windows remain outside local validation. Earlier publication/Vercel failures stem from repository permissions/settings. The documentation-link failures are repaired; full link checking passes locally and in CI.

Checklist

  • Changes covered by tests or validation above
  • Relevant compatibility changes documented in the changeset
  • Reviewed the complete local diff
  • Breaking changes described above

@cursor

cursor Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

PR Summary

High Risk
Breaking toolchain and Node minimum changes, plus new default V7 gas costs and revert-decoding behavior that affect local nodes, fees, and error surfaces across the SDK.

Overview
Bumps Fuel-owned toolchains and dependencies (VM ASM 0.66.4, Fuel Core 0.48.3, Forc 0.72.1, React/connectors 0.45.0) across packages, examples, and templates, refreshes the Core GraphQL schema, and raises the declared Node.js 20 minimum to 20.18 (CI default 20.20).

Local test nodes now wait for a successful GraphQL { health } query before resolving, with clearer startup failure handling: readiness polling is cancelled, temp snapshots and process groups are cleaned up, and parent signal handling is separated from caller cleanup so failed launches do not spuriously exit the CLI.

Revert/panic errors decode packed ABI 1.2+ panic indices while keeping legacy 1.1 and versionless behavior; decoding is scoped to the reverting program via receipt contract ID and optional mainContractId, so colliding panic indices no longer cross-contaminate messages.

Gas snapshots default to Core 0.48.3’s V7 table; explicit V4 overrides (including partial merges onto legacy V4 defaults) remain supported. The state-transition executor fixture is generated from the bundled Core WASM (including git-branch builds), and CI gains a coverage baseline job keyed to the PR’s exact base commit for diff reporting.

Reviewed by Cursor Bugbot for commit 2608579. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Vercel Deployment

Name Preview
fuels-template Failed ❌
ts-docs Failed ❌
ts-docs-api Failed ❌

@pixelcircuits pixelcircuits changed the title chore(deps): update Fuel-owned dependencies and toolchains chore(deps)!: update Fuel-owned dependencies and toolchains Oct 8, 2026
@pixelcircuits
pixelcircuits enabled auto-merge (squash) October 19, 2026 12:27
DefiCake
DefiCake previously approved these changes Oct 19, 2026
PraneshASP
PraneshASP previously approved these changes Oct 19, 2026

@nelitow nelitow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

1. git:<branch> Fuel Core mode breaks pretest and depcheck

buildFromGitBranch (internal/fuel-core/lib/shared.js:96-97) only copies fuel-core and VERSION into fuel-core-binaries/. The new generator (packages/fuel-gauge/scripts/generate-state-transition-bytecode.ts:4-6) reads fuel-core-binaries/fuel-core-wasm-executor.wasm. As a result, fuel-gauge pretest and root depcheck both fail with ENOENT whenever internal/fuel-core/VERSION is set to git:some/branch (the mode documented in CONTRIBUTING.md:124). That's exactly the mode used to test against an unreleased Core.

The Turbo cache has a related gap: globalDependencies: ["internal/fuel-core/VERSION"] doesn't change while a git: branch moves, so the cached .generated.ts goes stale.

Suggestion: build and copy the WASM executor in buildFromGitBranch, or at least fail fast with a clear message in the generator. Ideally key the cache on the WASM file itself rather than VERSION.

2. Node engines doesn't match the new 20.18 requirement

@solana/codecs-numbers@2.3.0 (pulled in by React/connectors 0.45.0) declares node: '>=20.18.0', and the PR description says scaffolded templates need it. But the root package.json and packages/create-fuels/package.json still declare ^20.0.0 || ^22.0.0 || ^24.0.0, and neither template declares engines. Someone on Node 20.14 can scaffold a project that then fails to install.

Suggestion: ^20.18.0 || ^22.0.0 || ^24.0.0 in create-fuels (and the root), plus a line in the changeset and create-fuels docs.

3. launchNode can orphan a node during the readiness wait

In packages/account/src/test-utils/launchNode.ts:356-360, any stderr chunk matching /error/i calls reject(...) without cleanup(), and it doesn't stop waitForGraphQL. On master this only mattered before the port bound, because the promise resolved right after. Now the window is up to 30s. If the health check succeeds after that reject, resolve is a no-op: the caller never gets a cleanup handle, so the fuel-core child and its temp dir stay alive until the test process exits.

Suggestion: route every failure through a single fail(err) that calls cleanup() and then reject, and have it stop the readiness loop. Also, when the child exits mid-wait, the error says only "did not become ready". Including child.exitCode / child.signalCode would make CI failures easier to diagnose.

4. ABI 1.2 detection is an exact string match

extract-tx-error.ts:153 checks abi.specVersion === '1.2'. Any later spec version (1.3+) would fall back to the legacy path, look up the raw 64-bit revert code in errorCodes, find nothing, and silently lose panic messages. Consider a "1.2 or later" comparison, or an explicit check against the legacy versions.

@pixelcircuits

Copy link
Copy Markdown
Contributor Author

@nelitow Addressed all four points from your review in ea1bcf357.

  1. Git Core builds and cache: buildFromGitBranch now copies the executor WASM from the same Cargo output path used by Core's release workflow. Git/Cargo failures or a missing executor fail before replacing installed artifacts. The installed WASM itself participates in Turbo's global hash; a dry-run probe confirms changed bytes invalidate pretest with the same git: VERSION, and restoring them restores the hash. Four packaging regressions cover fresh/existing checkouts, moving branches and failure cases (Git/Cargo are stubbed; a full Core source rebuild was not repeated).
  2. Node engines: declared ^20.18.0 || ^22.0.0 || ^24.0.0 in the root, create-fuels and both templates, and aligned the remaining published packages to satisfy the repository's existing common-engine policy. The changeset, create-fuels README and dApp guide document the new minimum; manifest lint passes.
  3. Readiness cleanup: startup failures now use a single guarded failure path that cancels the health request, kills the child and removes its snapshot. Exit/signal details are included when a child exits before readiness. Four regressions cover stderr, process errors, exit codes and signals while health is pending, including a late health success after failure; all four fail on the previous implementation and pass now.
  4. ABI versions: replaced the exact 1.2 comparison with numeric version detection for 1.2 and later. ABI 1.3/1.10 panic and contract-attribution regressions pass alongside legacy 1.1 and packed 1.2; the four new cases fail before the fix.

Validation: 2,804 Node tests pass (3 skipped, 6 todo), 18 Chromium decoder tests pass, all 28 package build tasks pass, and full lint/typechecks, package manifest checks and formatting pass. The PR description and changeset are updated. Fresh CI is separate from these local results.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

@pixelcircuits
pixelcircuits requested a review from nelitow October 21, 2026 17:05
@nelitow

nelitow commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Thanks, all four points look fixed. Locally on dc8d1842d, the targeted suites pass (84 tests).
Made a small browser dApp to confirms ABI 1.2 panic decoding and that a panic is reported from the contract that actually reverted.

The new fail() path in launchNode.ts causes two regressions:

  1. On macOS, launchNode hangs when fuel-core fails at startup. fail() calls cleanup() before reject(). If the child has already exited, process.kill(-pid) throws EPERM. launchNode's own uncaughtException handler swallows the error, so reject never runs. A wrong fuel-core version or a bad snapshot now hangs launchTestNode and fuels dev; on master it rejected immediately.
    • To reproduce, point fuelCorePath at fuel-core 0.46: the promise never settles, and Node exits with code 13 (unsettled top-level await).
  2. Bugbot's finding is real: fuels dev exits with 0 after a failed launch. fail() sends SIGTERM to the child, and the exit handler then calls process.exit(null), which exits with 0 before an async onFailure can finish.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 557e550. Configure here.

@pixelcircuits

Copy link
Copy Markdown
Contributor Author

@nelitow, both regressions are fixed in 557e55058 and 627c4c720, following up on your comment.

  • fail() now preserves the original launch rejection even if cleanup throws. Snapshot removal runs in finally, and cleanup removes this launch's process handlers, including its uncaughtException handler.
  • Startup failure no longer forcibly exits the parent from the child's exit handler. The CLI can await onFailure and then exit with status 1. Caller-initiated cleanup remains non-exiting; separate parent signal/exception handlers preserve first-signal shutdown (Ctrl+C exits 130) and unsuccessful exception exits.

Added regressions for cleanup EPERM during startup errors/early exit, plus real fuels dev subprocess tests that verify an asynchronous onFailure finishes before exit 1 and the first Ctrl+C exits 130. All 62 focused tests, both CLI regressions, 28 build tasks, and full lint/typechecks pass. Claude reviewed the final incremental changes and independently verified the focused/CLI tests and signal handling, with no further required changes.

The EPERM cases were simulated on Linux; I haven't run a native macOS check.

nelitow
nelitow previously approved these changes Oct 27, 2026

@nelitow nelitow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Re-verified on d841902f0. Both regressions are fixed: a fuel-core startup failure now rejects instead of hanging on macOS, and a failed fuels dev launch no longer exits 0 before the error handler finishes.

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report:

Lines Branches Functions Statements
78.13%(+0.94%) 70.55%(+1.08%) 77.43%(+1.19%) 78.15%(+0.93%)
Changed Files:
Ok File (✨=New File) Lines Branches Functions Statements
🔴 packages/account/src/providers/generated/operations.ts 94.97%
(+0.03%)
100%
(+0%)
83.92%
(+0%)
95.33%
(+0.02%)
🔴 packages/account/src/providers/utils/extract-tx-error.ts 59.52%
(+37.79%)
57.37%
(+51.97%)
75%
(+35%)
60.22%
(+36.61%)
🔴 packages/account/src/test-utils/launchNode.ts 97%
(+2.13%)
88.65%
(+6.3%)
93.75%
(+2.85%)
96%
(+2.56%)
🔴 packages/account/src/test-utils/setup-test-provider-and-wallets.ts 100%
(+0%)
95%
(-5%)
100%
(+0%)
100%
(+0%)
🔴 packages/program/src/contract.ts 84.21%
(+5.27%)
83.33%
(+0%)
62.5%
(+12.5%)
84.21%
(+5.27%)
🔴 packages/program/src/response.ts 5.45%
(+0.1%)
0%
(+0%)
0%
(+0%)
5.45%
(+0.1%)
🔴 packages/program/src/utils.ts 52.17%
(+42.65%)
30%
(+30%)
40%
(+40%)
52.17%
(+42.65%)
🔴 packages/program/src/functions/base-invocation-scope.ts 17.41%
(+17.41%)
8.33%
(+8.33%)
25%
(+25%)
17.58%
(+17.58%)
🔴 packages/program/src/functions/invocation-scope.ts 44.44%
(+44.44%)
16.66%
(+16.66%)
60%
(+60%)
42.1%
(+42.1%)

@pixelcircuits
pixelcircuits merged commit c822b1d into master Oct 27, 2026
24 of 28 checks passed
@pixelcircuits
pixelcircuits deleted the pixelcircuits/build/fuel-owned-dependency-upgrades branch October 27, 2026 21:44
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.

4 participants