You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently the portal is a pseudo-monorepo with packages/core and packages/metaport as internal-only packages. This adds build steps, config duplication, and workspace tooling overhead for no external benefit — nothing outside the app consumes these packages.
This ticket collapses everything into a single src/ tree, simplifies the build pipeline, and cleanly separates business logic from UI so the core can eventually be used headless (CLI, agents, scripts) without pulling in React or browser dependencies.
What changes
Move source files into the final structure:
packages/core/src → src/core/protocol
current src/core → src/core/services
packages/metaport/src → src/metaport
Normalize all imports to @/* → src/*:
@/core (legacy) → @/core/protocol
@skalenetwork/metaport → @/metaport
old local src/core imports → @/core/services
Consolidate build pipeline:
Single scripts/prepare-build.sh that copies config + metadata into src/data/ and runs generators
All network builds become: NETWORK_NAME=<network> bash scripts/prepare-build.sh && vite build
Fail fast on unknown NETWORK_NAME
Remove monorepo leftovers:
Delete all package-level package.json, tsconfig.json, vite.config.ts, .eslintrc, .prettierrc
Delete Storybook files and stories
Remove workspaces field and build:packages script from root package.json
Remove concurrently, vite-plugin-dts, Storybook deps, and other package-only dependencies
Consolidate to one .eslintrc.cjs, one tsconfig.json, one vite-env.d.ts
Merge duplicate cn utility into src/utils/cn.ts
Enforce headless-ready separation in src/core/:
The goal: src/core/protocol and src/core/services must be importable from a Node.js CLI or agent script without pulling in React, wagmi hooks, zustand, MUI, or any browser-only code. This is the foundation for a future headless CLI that can do bridge transfers, staking, delegation etc. programmatically.
Current coupling to clean up:
Action base class (action.ts) takes React.Dispatch setters (setActiveStep, setAmountErrorMessage, setBtnText) as constructor params. These should become generic callbacks (value: T) => void — no React types.
Action also takes wagmi's UseSwitchChainReturnType['switchChainAsync'] directly. Abstract this behind a SwitchChainFn type alias so the core doesn't import from wagmi.
network.ts imports wagmi/chains for mainnet chain definitions and UseSwitchChainReturnType. The chain data itself is fine (it's just objects with id, name, rpcUrls), but the import path should go through viem chain definitions, not wagmi — wagmi is a React library. Same for enforceNetwork() which takes the wagmi switchChain function.
ethers.ts has React hooks (useEthersSigner, useEthersProvider) — this entire file gets deleted in the viem migration ticket anyway, but make sure nothing replaces it with new React hooks inside src/core/.
community_pool.ts imports UseSwitchChainReturnType from wagmi.
MetaportStore.ts (zustand store) orchestrates action execution — this stays in src/metaport/ (UI layer), not in core.
Rules for the new structure:
src/core/** must have zero imports from react, wagmi, zustand, @mui/*, or any UI framework
src/core/** can import from viem (it's framework-agnostic) and @skalenetwork/* packages
Wallet interaction in core should go through injected interfaces, not framework-specific hooks
Add an eslint no-restricted-imports rule for src/core/** that blocks react, wagmi, zustand, and @mui
Update CI and docs:
Update workflows to remove packages/ directory assumptions
Update README.md with new dev/build commands
Add ARCHITECTURE.md with folder ownership rules and the core/UI boundary contract
Suggested PR split
PR1: move code into final folders with temporary compatibility aliases
Currently the portal is a pseudo-monorepo with
packages/coreandpackages/metaportas internal-only packages. This adds build steps, config duplication, and workspace tooling overhead for no external benefit — nothing outside the app consumes these packages.This ticket collapses everything into a single
src/tree, simplifies the build pipeline, and cleanly separates business logic from UI so the core can eventually be used headless (CLI, agents, scripts) without pulling in React or browser dependencies.What changes
Move source files into the final structure:
packages/core/src→src/core/protocolsrc/core→src/core/servicespackages/metaport/src→src/metaportNormalize all imports to
@/* → src/*:@/core(legacy) →@/core/protocol@skalenetwork/metaport→@/metaportsrc/coreimports →@/core/servicesConsolidate build pipeline:
scripts/prepare-build.shthat copies config + metadata intosrc/data/and runs generatorsNETWORK_NAME=<network> bash scripts/prepare-build.sh && vite buildNETWORK_NAMERemove monorepo leftovers:
package.json,tsconfig.json,vite.config.ts,.eslintrc,.prettierrcworkspacesfield andbuild:packagesscript from rootpackage.jsonconcurrently,vite-plugin-dts, Storybook deps, and other package-only dependencies.eslintrc.cjs, onetsconfig.json, onevite-env.d.tscnutility intosrc/utils/cn.tsEnforce headless-ready separation in
src/core/:The goal:
src/core/protocolandsrc/core/servicesmust be importable from a Node.js CLI or agent script without pulling in React, wagmi hooks, zustand, MUI, or any browser-only code. This is the foundation for a future headless CLI that can do bridge transfers, staking, delegation etc. programmatically.Current coupling to clean up:
Actionbase class (action.ts) takesReact.Dispatchsetters (setActiveStep,setAmountErrorMessage,setBtnText) as constructor params. These should become generic callbacks(value: T) => void— no React types.Actionalso takes wagmi'sUseSwitchChainReturnType['switchChainAsync']directly. Abstract this behind aSwitchChainFntype alias so the core doesn't import from wagmi.network.tsimportswagmi/chainsfor mainnet chain definitions andUseSwitchChainReturnType. The chain data itself is fine (it's just objects withid,name,rpcUrls), but the import path should go through viem chain definitions, not wagmi — wagmi is a React library. Same forenforceNetwork()which takes the wagmi switchChain function.ethers.tshas React hooks (useEthersSigner,useEthersProvider) — this entire file gets deleted in the viem migration ticket anyway, but make sure nothing replaces it with new React hooks insidesrc/core/.community_pool.tsimportsUseSwitchChainReturnTypefrom wagmi.MetaportStore.ts(zustand store) orchestrates action execution — this stays insrc/metaport/(UI layer), not in core.Rules for the new structure:
src/core/**must have zero imports fromreact,wagmi,zustand,@mui/*, or any UI frameworksrc/core/**can import fromviem(it's framework-agnostic) and@skalenetwork/*packagesno-restricted-importsrule forsrc/core/**that blocksreact,wagmi,zustand, and@muiUpdate CI and docs:
packages/directory assumptionsREADME.mdwith new dev/build commandsARCHITECTURE.mdwith folder ownership rules and the core/UI boundary contractSuggested PR split
@/*model, remove temp aliases, add lint guardsrc/data/*Acceptance criteria
mainnet,testnet,base,base-sepolia-testnet,qabun run lintpasses@/core(legacy meaning) or@skalenetwork/metaportpackages/directory existssrc/core/**has zero imports fromreact,wagmi,zustand, or@mui(enforced by eslint rule)/,/bridge,/chains,/ecosystem,/staking,/credits