Skip to main content

Bundle size: the core /wallet subpath and shell-safe helpers

An app has a SHELL (the code in the initial bundle: a connect flow, a failure presenter, a router) and LAZY chunks (the swap and pool screens) that use the whole SDK. This page says what the shell can import without paying for the rest.

The core /wallet subpath​

Since 2.0.0 the subpath lives in the peer package: @gala-chain/gswap-sdk/wallet no longer exists. @gala-chain/gala-core-sdk/wallet holds only the dependency-free wallet helpers and never loads the error catalog or the rest of the SDK:

import { isGalaErrorLike, isWalletUserRejection, walletDeclineCode } from '@gala-chain/gala-core-sdk/wallet';
  • isWalletUserRejection(value): did the person decline? (see Error Handling, "Did the person decline?")
  • walletDeclineCode(value): the wallet's own code (4001, 'ACTION_REJECTED'), the same reader that fills error.walletCode.
  • isGalaErrorLike(value) (and the type GalaErrorShape; named isGSwapErrorLike and GSwapErrorShape before 2.0.0): recognises an SDK error by a brand (Symbol.for('@gala-chain/gala-core-sdk/error')) or by the documented shape (a string code and an object params), never instanceof, so it also works when two copies of the SDK are in one page.

The core main entry re-exports the same functions from the same source modules. The subpath is built as its own self-contained bundle (dist/wallet.mjs, dist/wallet.cjs, dist/browser/wallet.js), so it does not depend on the app bundler's tree shaking at all: the built browser file is about 1.2 kB. packages/gala-core-sdk/tests/wallet_entry.test.ts and core's check:pack fail if the subpath's bundle contains any module besides its own, has any import, or exceeds 2048 bytes minified. core's exports["./wallet"] carries types, browser, import and require conditions, and the package declares "sideEffects": false.

Use it in every flow that has no SDK call of its own (a connect or registration flow, an analytics hook, a route guard). Measured with rolldown, a file whose only SDK import is isWalletUserRejection was 85,089 bytes minified from the rc.6 main entry (the function reached GSwapSDKError and the whole catalog) and is 986 bytes from the subpath.

Shell-safe helpers on the main entry​

The key, failure-view and wallet helpers (compositeKeyOf, tryParseCompositeKey, tryParseTokenClassKey, toFailureView, isWriteView, FAILURE_OUTCOMES, isFetchTransportFailure, isValidUniqueKey, isWalletUserRejection and the rest) moved to @gala-chain/gala-core-sdk in 2.0.0. Their budgets are enforced by core's tree-shaking test, packages/gala-core-sdk/tests/tree_shaking.test.ts (for example isWalletUserRejection 1,536 bytes). The table and budgets below are the helpers that stay in this package. The deciders reach core's light modules (the failure view and core's phase table) through the peer package.

The browser build of the main entry is a folder of preserved modules (one file per source module, dist/browser/), so a bundler that honours "sideEffects": false loads only the modules an import reaches. The import / require builds (dist/index.mjs, dist/index.cjs, for Node) are still one unminified file each.

A bundler puts every module that the shell and a lazy chunk both reach into the initial load WHOLE, so a helper is only as light as the module it lives in. The helpers below each live in a module that imports neither GSwapSDKError nor the error catalog:

ModuleHelpers
utils/classification.ts (imports nothing)classification, NOT_QUEUED_FINAL, notSentClassification, FailureClassification
utils/failure_classification.tsclassifyFailure, mayHaveBeenQueued (the table of every error code)
utils/failure_guards.tsisNoMarket, isRelayableRefusal, isUniqueKeyConsumed, isUnprovenAcknowledgement, isUninterpretedAnswer, chainErrorKeyOf
classes/pending_swap_fate.tspendingSwapFateFor, pendingSwapFateOfOutcome, PENDING_SWAP_FATES
classes/storage_adapters.ts (imports nothing)webStorageAdapter, storageAdapterFromChromeStorage
utils/tick_range.ts, utils/tick_align.ts (import nothing)isTickInRange, alignTickDownOrNaN, alignTickUpOrNaN
utils/tick_math.ts (only decimal.js and bignumber.js)tickToPrice, tickFromPrice, isSqrtPriceInRange, GC_MIN_TICK, GC_MAX_TICK
utils/confirmation_state.tsconfirmationStateOf, isConfirmationAborted, isConfirmationTimeout

A function that throws an SDK error is not shell-safe, by construction. Each has a non-throwing sibling in a light module: alignTickDownOrNaN / alignTickUpOrNaN for alignTickDown / alignTickUp, tryParseCompositeKey / tryParseTokenClassKey (from @gala-chain/gala-core-sdk) for parseTokenClassKey, and NaN from tickToPrice / tickFromPrice. A shell that needs the class itself imports the whole SDK lazily.

Budgets​

The initial-load budget of each helper, measured in the shell shape (a static import { X } in the entry plus a lazy chunk that does import * as sdk), minified bytes, through the packed tarball, with about 20% margin:

HelperBudget (measured)
classification, NOT_QUEUED_FINAL512 (363)
isTickInRange512 (294)
alignTickDownOrNaN, alignTickUpOrNaN512 (251)
ALL_FEE_TIERS, TICK_SPACING_BY_FEE512 (369)
webStorageAdapter, storageAdapterFromChromeStorage768 (478)
confirmationStateOf, isConfirmationAborted, isConfirmationTimeout1,792 (1,376)
isNoMarket, isRelayableRefusal, isUniqueKeyConsumed, chainErrorKeyOf2,560 (2,027)
classifyFailure, mayHaveBeenQueued9,216 (7,772)
pendingSwapFateFor9,728 (7,943)
tickToPrice, tickFromPrice, isSqrtPriceInRange62,464 (53,674)

The decider family carries the table of every error code (about 135 names, 4 kB of strings no minifier removes) and the phase table, but no class and no catalog templates. It reaches core's light modules (the failure view and core's phase table) through @gala-chain/gala-core-sdk, which stays a peer dependency of the app. The combined shell of this package's helpers measures 64,114 bytes. The three price functions are the two number libraries they compute with (decimal.js and bignumber.js, about 53 kB together) and about 1.3 kB of the SDK; an app that already ships both pays only the SDK's part.

tests/shell_shape.ts holds the table (SHELL_SAFE_HELPERS) and, when a helper breaks its budget, names the modules that came with it. tests/tree_shaking.test.ts runs it from src (rolldown), npm run check:pack from the packed tarball through the browser condition, and GSWAP_VITE_PACKAGE=<a vite package directory> npm test repeats it through a real Vite build (which adds Vite's own ~1.2 kB dynamic-import preload helper; the test subtracts it). A second test walks the run-time import graph of every light module from its source and fails on any path to the class or the catalog.

To print the numbers yourself:

tsx scripts/measure-shell-shape.ts classification compositeKeyOf
tsx scripts/measure-shell-shape.ts --entry @gala-chain/gswap-sdk --cwd /path/to/app classifyFailure
tsx scripts/measure-shell-shape.ts --vite /path/to/node_modules/vite classifyFailure