Skip to content

dapp-sdk top-level @walletconnect/sign-client import forces all consumers to bundle WalletConnect #1883

@rleonid

Description

@rleonid

Issue

@canton-network/dapp-sdk@1.1.0 declares @walletconnect/sign-client and @walletconnect/types as optional peer dependencies, signalling that consumers who don't use WalletConnectAdapter shouldn't have to install or bundle them. The current build, however, forces them on every consumer:

  // node_modules/@canton-network/dapp-sdk/dist/index.js
  import { WindowTransport } from '@canton-network/core-rpc-transport';
  import SignClient from '@walletconnect/sign-client';   // ← top-level, in index.js
  import * as coreWalletDappRpcClient from '@canton-network/core-wallet-dapp-rpc-client';
  // …

That static import in the entry module means any consumer that imports anything from @canton-network/dapp-sdk pulls SignClient and its transitive cone (~2.8 MB on disk; ~500–900 KB minified-bundled) into their bundle. It's also a mild conflict with the package.json signal that the peer dep is optional — without @walletconnect/sign-client installed, a fresh consumer's bundler errors at resolution time, not at runtime where the optional-peer contract would normally trigger.

This affects any dApp that:

  • only uses RemoteAdapter (CIP-103 wallet gateways) — but still pays for WalletConnect,
  • defers wallet sign-in behind a feature flag — but still ships WC in the critical-path bundle,
  • targets a build where WC isn't even wired (e.g. a minimal demo).

Proposed fix

Move the SignClient import (and any other @walletconnect/* references) out of src/index.ts and into src/adapter/walletconnect-adapter.ts, where it's actually used. The WalletConnectAdapter class already lives there. With the import collocated, consumers who never import WalletConnectAdapter get the WC chunk tree-shaken out by Rollup/esbuild/Webpack. Consumers who do import it still pay the same cost, but only when they ask for it.

Concretely, somewhere in src/index.ts the SDK currently does (or its bundler hoists):

  import SignClient from '@walletconnect/sign-client'

…in a path that ends up in the entry. If this lives only inside walletconnect-adapter.ts and index.ts does export { WalletConnectAdapter } from './adapter/walletconnect-adapter', tree-shaking can prune it.

A second, smaller win: change the WalletConnectAdapter constructor's call into SignClient.init({...}) to a dynamic import('@walletconnect/sign-client') inside the async establishSession method. Then even consumers who import WalletConnectAdapter for its type or for conditional registration only pay the cost when a session is actually established. This is closer to what the "optional peer dep" contract suggests.

Workaround

For now we lazy-import the wallet stack behind a React component boundary so it lands in a separate chunk that doesn't load until the wallet sign-in feature is engaged. Happy to share the diff if useful. But the upstream fix would let every consumer get this for free.

Thanks for the SDK

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions