Skip to content

refactor(client): compose action factories without as unknown casts - #224

Open
HusseinAdeiza wants to merge 1 commit into
genlayerlabs:v2-devfrom
HusseinAdeiza:fix/js-30-client-cast-chain
Open

HusseinAdeiza wants to merge 1 commit into
genlayerlabs:v2-devfrom
HusseinAdeiza:fix/js-30-client-cast-chain

Conversation

@HusseinAdeiza

Copy link
Copy Markdown

Closes #30

What

createClient composed seven action factories, and every hop went through as unknown as GenLayerClient<GenLayerChain>. Each factory was declared against the full GenLayerClient even though it reads a small slice of it, so the composition could not type-check at any intermediate step and the casts were load-bearing.

This names those slices and declares them as the factory parameters, so the chain type-checks hop by hop:

type factories
ClientRequester account, chain, request
TransactionActionInput adds the underlying getTransaction
TransactionReader adds the GenLayer-typed getTransaction
ContractWriter adds the gas/nonce/sendRawTransaction helpers
ChainSwitcher writable chain, for connect()

One narrowing assertion remains on the return, because a spread of extended viem clients cannot prove the full public shape. That is one cast instead of seven double-casts.

Two real mismatches the casts were hiding

  • transactionActions reads viem's getTransaction, then installs its own GenLayer-typed override as a returned method. Its input therefore cannot be typed as the override it produces, hence TransactionActionInput being separate from TransactionReader. Typing it the other way would have made receiptActions (which does consume the override) unsatisfiable.
  • ClientConfig.chain was a loose structural type, so GenLayerChain never propagated past createClient and every chain consumer downstream needed its own cast. It is now chain?: GenLayerChain.

Notes

  • No behaviour change. ClientRequestFn still checks method names and argument shapes; the factories cast their own results either way, so this does not loosen the RPC surface, only the client composition.
  • transaction.result as number in the Studio path is a narrowing of a value the raw RPC record types as unknown.

Validation

  • npx tsc --noEmit -p tsconfig.json — clean
  • npx vitest run --typecheck — 209 passed, no type errors
  • npm run build — clean
  • npm run check:chains — no drift
  • npx eslint src/ — clean

tests/client-action-surfaces.test.ts is load-bearing in the type-check sense, which is the only sense available for a type-level change: it fails to compile against the pre-fix tree with 5 errors (has no exported member 'ClientRequester' and friends) and compiles clean after. Its runtime assertions pin that the built client still exposes every factory's methods, which a cast-only regression would not catch.

Every action factory was declared against the full `GenLayerClient`, so
`createClient` had to launder each intermediate step through
`as unknown as GenLayerClient<GenLayerChain>`: seven double-casts that
disabled type checking exactly where the composition is most fragile.

Each factory only reads a slice of the client, so those slices are now
named (`ClientRequester`, `TransactionActionInput`, `TransactionReader`,
`ContractWriter`, `ChainSwitcher`) and declared as the factory parameters.
`createClient` type-checks hop by hop and needs a single narrowing
assertion on the return.

Two real mismatches surfaced once the casts stopped hiding them:

- `transactionActions` reads viem's `getTransaction` and then installs its
  own GenLayer-typed override as a returned method, so its input cannot be
  typed as the override it produces (`TransactionActionInput`).
- `ClientConfig.chain` was a loose structural type, so `GenLayerChain` never
  propagated and every chain consumer needed a cast.

No behaviour change. `ClientRequestFn` keeps checking method names and
argument shapes; the factories cast their own results either way.

Tests: 209 passed, `tsc --noEmit` clean, typecheck clean, build and
check:chains clean. `tests/client-action-surfaces.test.ts` fails to compile
against the pre-fix tree (5 errors), so it pins the narrowed surfaces.

Closes genlayerlabs#30
@coderabbitai

coderabbitai Bot commented Sep 29, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: e2bb9b65-d0b3-47c6-b2fc-01c5037c3348

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

This branch has not been deployed

No deployments
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.

1 participant