@phoenix-wallet/core
v1.0.7
Published
Core interfaces and utilities for Phoenix Wallet - A unified multi-chain wallet adapter
Maintainers
Readme
@phoenix-wallet/core
Core package for Phoenix Wallet - a unified multi-chain wallet adapter library.
Overview
This package provides the foundational interfaces, types, contexts, and hooks that are shared across all blockchain implementations in the Phoenix Wallet ecosystem. It contains no blockchain-specific dependencies, making it lightweight and modular.
Features
- 🔌 Universal Interfaces: Common interfaces for wallets, connectors, contracts, and chains
- 🎯 Type Safety: Comprehensive TypeScript types for all wallet operations
- 🪝 React Hooks: Base hooks for wallet connection and event handling
- 🔄 Event System: Unified event handling across all chains
- 📦 Zero Blockchain Dependencies: Pure abstractions with no chain-specific code
- 📝 Configurable Logger: Built-in logging system with multiple log levels for debugging and monitoring
Installation
# Using pnpm
pnpm add @phoenix-wallet/core
# Using npm
npm install @phoenix-wallet/core
# Using yarn
yarn add @phoenix-wallet/coreCore Concepts
Chain Types
The package supports multiple blockchain types:
enum ChainType {
EVM = 'evm',
SOLANA = 'solana',
APTOS = 'aptos',
SUI = 'sui'
}Interfaces
IConnector
Base interface for all wallet connectors:
interface IConnector {
id: string;
name: string;
logo: string;
chainType: ChainType;
status: ConnectorStatus;
activate(): Promise<string[]>;
deactivate(): Promise<void>;
isInstalled(): boolean;
switchChainId(chainId: string): Promise<void>;
}IWallet
Base interface for wallet instances:
interface IWallet<TAddress, TChain, TConnector, TClient> {
address: TAddress;
chain: TChain;
connector: TConnector;
walletClient: TClient;
getBalance(): Promise<string>;
signMessage(message: string | Uint8Array): Promise<string>;
sendTransaction(transaction: any): Promise<string>;
}Contract
Base class for smart contract interactions:
abstract class Contract {
address: string;
abi?: any;
abstract call(method: string, params: any[]): Promise<any>;
abstract send(method: string, params: any[]): Promise<string>;
}React Hooks
useWalletConnectors
Access all registered wallet connectors:
import { useWalletConnectors } from '@phoenix-wallet/core';
function MyComponent() {
const { connectors, chainConfigs } = useWalletConnectors();
return (
<div>
{connectors.map(connector => (
<div key={connector.id}>{connector.name}</div>
))}
</div>
);
}useWalletConnectorEvent
Listen to wallet connector events:
import { useWalletConnectorEvent } from '@phoenix-wallet/core';
function MyComponent() {
useWalletConnectorEvent('metamask-evm', {
onAccountChanged: (connectorId, address) => {
console.log('Account changed:', address);
},
onChainChanged: (connectorId, chainId) => {
console.log('Chain changed:', chainId);
},
onDisconnect: (connectorId) => {
console.log('Disconnected');
}
});
return <div>Listening to wallet events...</div>;
}useWalletBase
Base hook for building custom wallet hooks:
import { useWalletBase } from '@phoenix-wallet/core';
function useCustomWallet(connectorId: string) {
return useWalletBase(
connectorId,
(connector, address, chainId, chainConfigs) => {
// Create and return your wallet instance
return createWalletInstance(connector, address, chainId);
},
{
onAccountChanged: (connectorId, address, wallet) => {
console.log('Account changed');
},
onChainChanged: (connectorId, chainId, wallet) => {
console.log('Chain changed');
}
}
);
}Context Provider
WalletProvider
Wrap your app with the WalletProvider to enable wallet functionality:
import { WalletProvider, LogLevel } from '@phoenix-wallet/core';
function App() {
return (
<WalletProvider
connectors={[/* your connectors */]}
chainConfigs={[/* your chain configs */]}
loggerConfig={{
enabled: true,
level: LogLevel.INFO
}}
>
<YourApp />
</WalletProvider>
);
}Logger
Phoenix Wallet includes a configurable logging system that allows you to control the verbosity of library logs. This is useful for debugging, monitoring, and troubleshooting.
Quick Start
Enable logging by passing loggerConfig to the WalletProvider:
import { WalletProvider, LogLevel } from '@phoenix-wallet/core';
<WalletProvider
connectors={connectors}
chainConfigs={chainConfigs}
loggerConfig={{
enabled: true,
level: LogLevel.INFO,
prefix: '[Phoenix Wallet]'
}}
>
{children}
</WalletProvider>Log Levels
enum LogLevel {
NONE = 0, // No logs
ERROR = 1, // Only errors
WARN = 2, // Warnings and errors
INFO = 3, // Info, warnings, and errors (default)
DEBUG = 4, // All logs including debug messages
}Examples
Development Mode (All Logs):
loggerConfig={{ enabled: true, level: LogLevel.DEBUG }}Production Mode (Errors Only):
loggerConfig={{ enabled: true, level: LogLevel.ERROR }}Disabled (Default):
// Simply omit loggerConfig or set enabled: false
loggerConfig={{ enabled: false }}Using Logger Directly
You can also use the logger in your own code:
import { logger, LogLevel } from '@phoenix-wallet/core';
// Configure
logger.configure({ enabled: true, level: LogLevel.DEBUG });
// Use
logger.debug('Debug message');
logger.info('Info message');
logger.warn('Warning message');
logger.error('Error message', error);For detailed documentation, see LOGGER.md.
Types
ConnectorStatus
enum ConnectorStatus {
DISCONNECTED = 'disconnected',
CONNECTING = 'connecting',
CONNECTED = 'connected',
UNAVAILABLE = 'unavailable'
}IChainConfig
interface IChainConfig {
id: string;
name: string;
chainId?: number | string;
rpcUrl?: string;
nativeCurrency?: {
name: string;
symbol: string;
decimals: number;
};
blockExplorerUrl?: string;
}Creating Your Universal useWallet Hook
The useWalletBase hook is designed to be wrapped in your application to create a universal wallet hook that works across all chains. Here's how to create your own useWallet hook:
Step 1: Create src/hooks/useWallet.ts
import {
useWalletBase,
ChainType,
type UseWalletCallbacks,
type WalletState,
type IConnector,
type IChainConfig
} from '@phoenix-wallet/core';
import { EvmWallet, EvmChain, EvmConnector } from '@phoenix-wallet/evm';
import { SolanaWallet, SolanaChain, SolanaConnector } from '@phoenix-wallet/solana';
import { AptosWallet, AptosChain, AptosConnector } from '@phoenix-wallet/aptos';
import { SuiWallet, SuiChain, SuiConnector } from '@phoenix-wallet/sui';
/**
* Universal wallet hook - works with any chain
* Just pass the connectorId and it automatically handles the rest!
*
* @example
* ```tsx
* // Works for any chain!
* const { wallet, connect, disconnect, isConnected } = useWallet('metamask-evm');
* const { wallet, connect, disconnect, isConnected } = useWallet('phantom-solana');
* const { wallet, connect, disconnect, isConnected } = useWallet('petra-aptos');
* const { wallet, connect, disconnect, isConnected } = useWallet('phantom-sui');
* ```
*/
export function useWallet<W = any>(
connectorId: string,
callbacks?: UseWalletCallbacks<any>
): WalletState<W> {
return useWalletBase<any>(
connectorId,
(connector: IConnector, address: string, chainId: string, chainConfigs: IChainConfig[]) => {
try {
// Find the chain config from the provided chainConfigs
const chainConfig = chainConfigs.find(
(c) => c.id === chainId || c.chainId?.toString() === chainId
);
if (!chainConfig) {
console.warn(`Chain with ID ${chainId} not found in chainConfigs`);
return null;
}
// Create the appropriate wallet based on chain type
switch (connector.chainType) {
case ChainType.EVM: {
const evmConnector = connector as EvmConnector;
const evmChain = new EvmChain(chainConfig.name, chainConfig as any);
const walletClient = (evmConnector as any).createWalletClient(evmChain);
return new EvmWallet(address, evmChain, evmConnector, walletClient) as any;
}
case ChainType.SOLANA: {
const solanaConnector = connector as SolanaConnector;
const solanaChain = new SolanaChain(chainConfig.name, chainConfig as any);
const walletClient = (solanaConnector as any).createWalletClient(solanaChain);
return new SolanaWallet(address, solanaChain, solanaConnector, walletClient) as any;
}
case ChainType.APTOS: {
const aptosConnector = connector as AptosConnector;
const aptosChain = new AptosChain(chainConfig.name, chainConfig as any);
const walletClient = (aptosConnector as any).createWalletClient(aptosChain);
return new AptosWallet(address, aptosChain, aptosConnector, walletClient) as any;
}
case ChainType.SUI: {
const suiConnector = connector as SuiConnector;
const suiChain = new SuiChain(chainConfig.name, chainConfig as any);
const walletClient = (suiConnector as any).createWalletClient(suiChain);
return new SuiWallet(address, suiChain, suiConnector, walletClient) as any;
}
default:
console.warn(`Unsupported chain type: ${connector.chainType}`);
return null;
}
} catch (error) {
console.error('Failed to create wallet:', error);
return null;
}
},
callbacks
);
}Step 2: Use Your Universal Hook
Now you can use the same hook for all chains:
import { useWallet } from '@/hooks/useWallet';
function MyComponent() {
// EVM wallet (MetaMask, Coinbase, etc.)
const {
wallet: evmWallet,
isConnected: evmConnected,
connect: connectEvm,
disconnect: disconnectEvm
} = useWallet('metamask-evm');
// Solana wallet (Phantom, Solflare, etc.)
const {
wallet: solWallet,
isConnected: solConnected,
connect: connectSol
} = useWallet('phantom-solana');
// Aptos wallet (Petra, Pontem, etc.)
const {
wallet: aptWallet,
isConnected: aptConnected,
connect: connectApt
} = useWallet('petra-aptos');
// Sui wallet (Phantom, Suiet, etc.)
const {
wallet: suiWallet,
isConnected: suiConnected,
connect: connectSui
} = useWallet('phantom-sui');
return (
<div>
{/* All wallets use the same interface! */}
<button onClick={connectEvm}>Connect MetaMask</button>
<button onClick={connectSol}>Connect Phantom (Solana)</button>
<button onClick={connectApt}>Connect Petra</button>
<button onClick={connectSui}>Connect Phantom (Sui)</button>
</div>
);
}Step 3: Use Wallet Methods
All wallets share the same interface:
const { wallet, isConnected } = useWallet('metamask-evm');
if (isConnected && wallet) {
// Get balance (works for all chains)
const balance = await wallet.getBalance();
// Sign message (works for all chains)
const signature = await wallet.signMessage('Hello, World!');
// Send transaction (works for all chains)
const txHash = await wallet.sendTransaction(transaction);
}Why Create Your Own Hook?
- Flexibility: You control the wallet creation logic
- Customization: Add your own error handling, logging, or analytics
- Type Safety: Customize types for your specific needs
- Chain Support: Only include chains you actually use
- Bundle Size: Tree-shaking removes unused chain packages
Alternative: Chain-Specific Hooks
If you only need one chain, you can create a simpler hook:
// src/hooks/useEvmWallet.ts
import { useWalletBase, type UseWalletCallbacks } from '@phoenix-wallet/core';
import { EvmWallet, EvmChain, EvmConnector } from '@phoenix-wallet/evm';
export function useEvmWallet(
connectorId: string,
callbacks?: UseWalletCallbacks<EvmWallet>
) {
return useWalletBase<EvmWallet>(
connectorId,
(connector, address, chainId, chainConfigs) => {
const chainConfig = chainConfigs.find(c => c.id === chainId);
if (!chainConfig) return null;
const evmConnector = connector as EvmConnector;
const evmChain = new EvmChain(chainConfig.name, chainConfig as any);
const walletClient = (evmConnector as any).createWalletClient(evmChain);
return new EvmWallet(address, evmChain, evmConnector, walletClient);
},
callbacks
);
}Usage with Chain-Specific Packages
This core package is designed to be used alongside chain-specific packages:
import { WalletProvider, ChainType } from '@phoenix-wallet/core';
import { MetamaskEvmConnector } from '@phoenix-wallet/evm';
import { SolanaConnector } from '@phoenix-wallet/solana';
import { PetraAptosConnector } from '@phoenix-wallet/aptos';
import { SuiStandardConnnector } from '@phoenix-wallet/sui';
const connectors = [
new MetamaskEvmConnector(/* config */),
new SolanaConnector(/* config */),
new PetraAptosConnector(/* config */),
new SuiStandardConnnector(/* config */)
];
function App() {
return (
<WalletProvider connectors={connectors} chainConfigs={chainConfigs}>
<YourApp />
</WalletProvider>
);
}Related Packages
- @phoenix-wallet/evm - EVM blockchain support (Ethereum, BSC, Polygon, etc.)
- @phoenix-wallet/solana - Solana blockchain support
- @phoenix-wallet/aptos - Aptos blockchain support
- @phoenix-wallet/sui - Sui blockchain support
- @phoenix-wallet/privy - Privy authentication integration
License
MIT
Contributing
Contributions are welcome! Please read our contributing guidelines before submitting a PR.
