Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Koniverse/SubWallet-Extension/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The SubWallet Extension background runs as a service worker that handles all core wallet operations including keyring management, blockchain interactions, transaction processing, and state management. It operates independently of the UI and manages communication with both content scripts and the extension popup.
Entry Point
The background service initializes in packages/extension-koni/src/background.ts:
import { SWHandler } from '@subwallet/extension-base/koni/background/handlers';
import { ActionHandler } from '@subwallet/extension-koni/helper/ActionHandler';
import keyring from '@subwallet/ui-keyring';
import { cryptoWaitReady } from '@polkadot/util-crypto';
// Set handler
const actionHandler = ActionHandler.instance;
actionHandler.setHandler(SWHandler.instance);
cryptoWaitReady()
.then((): void => {
const koniState = SWHandler.instance.state;
// Initialize environment config
koniState.initEnvConfig(envConfig);
// Load all keyring data
keyring.loadAll({
store: new AccountsStore(),
type: 'sr25519',
password_store: new KeyringStore()
});
keyring.restoreKeyringPassword().finally(() => {
koniState.updateKeyringState();
});
koniState.eventService.emit('crypto.ready', true);
// Initialize state after first active message
actionHandler.waitFirstActiveMessage.then(() => {
koniState.init().catch(console.error);
});
});
Source: packages/extension-koni/src/background.ts:25-59
Core Components
1. ActionHandler
The ActionHandler is a singleton that manages the lifecycle of port connections and message routing.
Location: packages/extension-koni/src/helper/ActionHandler.ts
export class ActionHandler {
private mainHandler?: SWHandler;
private connectionMap: Record<string, string> = {};
private isActive = false;
private isFullActive = false;
private sleepTimeout?: NodeJS.Timeout;
get isContentConnecting(): boolean {
return Object.values(this.connectionMap).some((v) => v === PORT_CONTENT);
}
get isExtensionConnecting(): boolean {
return Object.values(this.connectionMap).some((v) => v === PORT_EXTENSION);
}
public handlePort(port: chrome.runtime.Port): void {
assert([PORT_CONTENT, PORT_EXTENSION].includes(port.name));
const portId = this._getPortId(port);
port.onMessage.addListener((data) => {
this._onPortMessage(port, data, portId).catch(console.error);
});
port.onDisconnect.addListener(() => {
this._onPortDisconnect(port, portId);
});
}
}
Source: packages/extension-koni/src/helper/ActionHandler.ts:16-148
Key Responsibilities:
- Manages port connections from content scripts and UI
- Tracks active connections and handles sleep/wake cycles
- Routes messages to the appropriate handler
- Implements a 60-second timeout before entering sleep mode
2. SWHandler
The SWHandler orchestrates all background handlers and routes requests.
Location: packages/extension-base/src/koni/background/handlers/index.ts
export class SWHandler {
_state?: KoniState;
_extensionHandler?: KoniExtension;
_tabsHandler?: KoniTabs;
_mobileHandler?: Mobile;
public get state(): KoniState {
if (!this._state) {
this._state = new KoniState();
}
return this._state;
}
public handle<TMessageType extends MessageTypes>(
{ id, message, request }: TransportRequestMessage<TMessageType>,
port: chrome.runtime.Port
): void {
const isMobile = port.name === PORT_MOBILE;
const isExtension = port.name === PORT_EXTENSION;
const promise = isMobile
? this.mobileHandler.handle(id, message, request, port)
: isExtension
? this.extensionHandler.handle(id, message, request, port)
: this.tabHandler.handle(id, message, request, from, port);
promise
.then((response): void => {
port.postMessage({ id, response, sender: 'BACKGROUND' });
})
.catch((error: ProviderError): void => {
port.postMessage({
error: error.message,
errorCode: error.code,
id,
sender: 'BACKGROUND'
});
});
}
}
Source: packages/extension-base/src/koni/background/handlers/index.ts:14-88
3. KoniState
The central state manager that coordinates all services.
Location: packages/extension-base/src/koni/background/handlers/State.ts
export default class KoniState {
private injectedProviders = new Map<chrome.runtime.Port, ProviderInterface>();
private readonly providers: Providers;
private externalRequest: Record<string, ExternalRequestPromise> = {};
// Services
public eventService: EventService;
public chainService: ChainService;
public balanceService: BalanceService;
public priceService: PriceService;
public transactionService: TransactionService;
public earningService: EarningService;
public swapService: SwapService;
public walletConnectService: WalletConnectService;
public requestService: RequestService;
public keyringService: KeyringService;
// ... and more
public async init(): Promise<void> {
// Initialize all services
await this.eventService.init();
await this.chainService.init();
await this.balanceService.init();
// ...
}
public async wakeup(isFullActive: boolean): Promise<void> {
// Wake up services from sleep mode
}
public async sleep(): Promise<void> {
// Put services into sleep mode
}
}
Source: packages/extension-base/src/koni/background/handlers/State.ts:96-100
Background Services
KoniState manages multiple specialized services:
Core Services
- EventService: Event bus for inter-service communication
- ChainService: Manages blockchain connections and metadata
- BalanceService: Tracks account balances across chains
- PriceService: Fetches and caches token prices
- KeyringService: Manages accounts and keys
- TransactionService: Handles transaction creation and submission
Feature Services
- EarningService: Manages staking and yield positions
- SwapService: Handles token swaps across protocols
- NftService: NFT collection and item management
- WalletConnectService: WalletConnect protocol integration
- RequestService: Manages dApp authorization and signing requests
- HistoryService: Transaction history tracking
- MultisigService: Multisig account operations
Port Communication
The background service communicates through Chrome runtime ports:
Port Types
const PORT_CONTENT = 'koni-content'; // Content script connections
const PORT_EXTENSION = 'koni-extension'; // UI popup connections
const PORT_MOBILE = 'mobile'; // Mobile app connections
Source: packages/extension-base/src/defaults.ts:17-19
Connection Lifecycle
- Connection: Content script or UI connects via
chrome.runtime.connect()
- Registration: ActionHandler registers the port and assigns an ID
- Activation: First message triggers service wakeup
- Message Handling: Messages routed through SWHandler to appropriate handler
- Disconnection: Port disconnect triggers cleanup and sleep timeout
- Sleep: After 60 seconds of no connections, services enter sleep mode
Message Flow
UI/Content Script
|
v
Port.postMessage
|
v
ActionHandler
|
v
SWHandler.handle
|
v
+-----------------+
| Route by port |
+-----------------+
|
+---> ExtensionHandler (UI messages)
|
+---> TabsHandler (Content script messages)
|
+---> MobileHandler (Mobile messages)
|
v
KoniState (services)
|
v
Port.postMessage (response)
Service Lifecycle
Initialization
// Wait for crypto libraries
cryptoWaitReady()
.then(() => {
// 1. Initialize keyring
keyring.loadAll({ ... });
// 2. Wait for first active message
actionHandler.waitFirstActiveMessage.then(() => {
// 3. Initialize all services
koniState.init();
});
});
Sleep/Wake Cycle
Wake Conditions:
- First port connection
- Message requiring active services
- Full wake for
pri() or mobile() messages
Sleep Conditions:
- All ports disconnected
- 60-second timeout elapsed
- Services pause background operations
Global Variables (Development)
In non-production mode, debug helpers are exposed:
if (!isProductionMode) {
globalThis.KoniState = SWHandler.instance.state;
globalThis.KoniHandler = SWHandler.instance;
}
Source: packages/extension-koni/src/background.ts:74-77
Access in console: KoniState, KoniHandler
Best Practices
- Service Initialization: Always check if services are initialized before use
- Error Handling: Wrap service calls in try-catch blocks
- Memory Management: Services should clean up subscriptions on sleep
- Port Management: Always handle port disconnect events
- State Synchronization: Use event service for cross-service communication
Common Patterns
Adding a New Service
- Create service class extending base service
- Add service instance to KoniState
- Initialize service in
KoniState.init()
- Implement
wakeup() and sleep() methods
- Register message handlers in appropriate handler class
Service Communication
// Use event service for cross-service communication
this.eventService.emit('balance.updated', balanceData);
this.eventService.on('balance.updated', (data) => {
// Handle balance update
});