Universal blockchain operating layer

Build once across Ethereum, Solana, and every chain that comes next.

OmniKit is no longer just a wallet toolkit. It is a chain-abstracted development framework for JavaScript and TypeScript applications. Developers write against one predictable SDK while adapters translate Ethereum gas, Solana compute, wallet standards, contract ABIs, program IDLs, confirmations, balances, and future chain models behind the scenes.

One API

Wallets, balances, transactions, contracts, events, React hooks, and server utilities now share the same chain-agnostic vocabulary.

Adapter-first

Core contains orchestration only. Every chain plugs in through a strict ChainAdapter contract, so new networks do not require SDK rewrites.

Less Web3 glue

Teams stop maintaining separate Ethereum, Solana, and future-chain integration paths for the same product workflow.

No core changes

Sui, Aptos, Starknet, Cosmos, Bitcoin L2s, and other systems can be added by registering adapters and capabilities.

Quick Start

Install the core package, the adapters you need, and the React or server package for your runtime. The public API stays stable as you add chains.

terminal
npm install @watchupltd/omnikit @watchupltd/omnikit-types
npm install @watchupltd/omnikit-ethereum @watchupltd/omnikit-solana
npm install @watchupltd/omnikit-react @watchupltd/omnikit-server
app.ts
import { createOmniKit } from '@watchupltd/omnikit';

const omnikit = createOmniKit({
  chains: ['ethereum', 'solana'],
  storage: { type: 'localStorage', prefix: 'myapp' },
  autoConnect: true
});

const wallet = await omnikit.wallet.connect();
const [account] = wallet.accounts;

const balance = await omnikit.wallet.balance({
  chain: account.chain,
  account: account.address
});

const tx = await omnikit.transaction.create({
  chain: account.chain,
  to: 'recipient-address',
  value: '0.01'
});

const receipt = await tx.send();
await tx.wait();

The Universal API

Application code should not ask whether it is dealing with an EVM transaction, a Solana transaction, or a future Move-based transaction. It should describe intent. OmniKit maps that intent to the active chain.

WorkflowBefore OmniKitWith OmniKit
WalletsMetaMask, Phantom, WalletConnect, injected providers, mobile wallet branchesomnikit.wallet.connect()
Balancesethers providers, Solana connections, token account parsingomnikit.wallet.balance({ chain, account, token })
TransactionsGas, nonces, blockhashes, fee payers, compute units, serializationomnikit.transaction.create(...).send().wait()
Contractsethers.Contract vs Anchor Program vs future contract clientsomnikit.contract(...).read() / write()
Server jobsDifferent indexers, RPC pools, relayers, and watchers per chaincreateOmniServer().watchTransfers(...)
const wallet = await omnikit.wallet.connect();
const accounts = await omnikit.wallet.accounts();
const signature = await omnikit.wallet.signMessage({ message: 'Sign in' });
const balance = await omnikit.wallet.balance({ account: accounts[0].address });

Architecture

OmniKit is split into a functional core and chain adapters. The core package owns state, routing, transaction handles, contract clients, and registry behavior. Chain packages own the details that should never leak into app code.

@watchupltd/omnikit-types

Defines ChainAdapter, UniversalTransactionRequest, TransactionReceipt, ContractDescriptor, BalanceResult, and shared primitives.

@watchupltd/omnikit

Provides OmniClient, OmniKit, ChainAdapterRegistry, transaction handles, and wallet-adapter bridging.

@watchupltd/omnikit-ethereum

Translates EIP-1193 wallets, EVM transactions, ABI reads and writes, gas, and confirmations.

@watchupltd/omnikit-solana

Translates Phantom/Solflare, Solana transactions, program instructions, blockhashes, compute fees, and SPL balances.

@watchupltd/omnikit-react

Exposes unified hooks. No public Ethereum-only or Solana-only hooks are required for product code.

@watchupltd/omnikit-server

Adds RPC pooling, relaying, caching, transfer watching, and indexing utilities for backend workflows.

React Integration

The React package now mirrors the universal runtime. Components ask for wallet state, balances, transactions, and contracts without branching on Ethereum or Solana.

providers.tsx
import { OmniKitProvider, useWallet, useTransaction, useContract } from '@watchupltd/omnikit-react';
import { createOmniKit } from '@watchupltd/omnikit';

const omnikit = createOmniKit({ chains: ['ethereum', 'solana'] });

export function Providers({ children }) {
  return <OmniKitProvider config={omnikit}>{children}</OmniKitProvider>;
}

function CheckoutButton() {
  const { connectWallet, address, chain, isConnected } = useWallet();
  const tx = useTransaction();

  async function pay() {
    if (!isConnected) await connectWallet();
    await tx.send({ chain, to: 'merchant-address', value: '0.01' });
  }

  return <button onClick={pay}>{address ? 'Pay now' : 'Connect'}</button>;
}

Server SDK

The new server package gives backend services the same abstraction layer: RPC pools, rate limiting, transaction relaying, event subscriptions, caching, and indexing workflows.

server.ts
import { createOmniServer } from '@watchupltd/omnikit-server';
import { ethereumAdapter, solanaAdapter } from './adapters';

const server = createOmniServer({
  adapters: [ethereumAdapter, solanaAdapter],
  rpc: {
    ethereum: ['https://eth.llamarpc.com'],
    solana: ['https://api.mainnet-beta.solana.com']
  },
  rateLimit: { maxConcurrent: 16 },
  cacheTtlMs: 15000
});

await server.watchTransfers({
  chain: 'ethereum',
  address: treasuryAddress,
  onTransfer: async (event) => saveTransfer(event)
});

await server.relay({ chain: 'solana', to, value: '1' });

Adding Future Chains

A new blockchain integration should require a new adapter, not a new application architecture. Implement the ChainAdapter contract, declare capabilities, and register it with OmniKit.

sui-adapter.ts
import type { ChainAdapter } from '@watchupltd/omnikit-types';

export const suiAdapter: ChainAdapter = {
  id: 'sui-wallet',
  metadata: {
    id: 'sui',
    namespace: 'move',
    name: 'Sui',
    nativeCurrency: { name: 'Sui', symbol: 'SUI', decimals: 9 }
  },
  capabilities: {
    wallets: true, transactions: true, messages: true, contracts: true,
    events: true, tokens: true, nfts: true, networkSwitching: true,
    localAccounts: false
  },
  async connectWallet() { /* native wallet -> UniversalWalletConnection */ },
  async getBalance() { /* native balance -> BalanceResult */ },
  async createTransaction(request) { /* universal request -> native tx */ },
  async sendTransaction(transaction) { /* native hash -> TransactionReceipt */ },
  async waitForTransaction(hash) { /* native finality -> TransactionReceipt */ },
  async readContract(request) { /* ABI, IDL, or Move call -> value */ },
  async writeContract(request) { /* native write -> TransactionReceipt */ }
};

Type System

The most important type in the new architecture is ChainAdapter. It is the boundary between chain-specific code and the universal developer experience.

omni-types.d.ts
interface ChainAdapter {
  readonly id: AdapterId;
  readonly metadata: ChainMetadata;
  readonly capabilities: ChainCapabilities;

  connectWallet(request?: WalletConnectionRequest): Promise<UniversalWalletConnection>;
  disconnectWallet(): Promise<void>;
  getAccounts(): Promise<readonly UniversalAccount[]>;

  signMessage(request: SignMessageRequest): Promise<SignatureResult>;
  getBalance(request?: BalanceRequest): Promise<BalanceResult>;

  createTransaction(request: UniversalTransactionRequest): Promise<PreparedTransaction>;
  sendTransaction(tx: UniversalTransactionRequest | PreparedTransaction | SignedTransaction): Promise<TransactionReceipt>;
  waitForTransaction(hash: string, options?: TransactionWaitOptions): Promise<TransactionReceipt>;

  readContract?<TResult = unknown>(request: ContractReadRequest): Promise<TResult>;
  writeContract?(request: ContractWriteRequest): Promise<TransactionReceipt>;
}

Migration Notes

Existing wallet-adapter APIs still work, but new application code should move to the universal API. This lets teams keep shipping while they migrate product surfaces one workflow at a time.

LegacyPreferredReason
omnikit.connect('ethereum')omnikit.wallet.connect({ chain: 'ethereum' })Returns normalized wallet connection data.
omnikit.getBalance()omnikit.wallet.balance({ chain, account })Works for native and token balances across chains.
omnikit.sendTransaction(rawTx)omnikit.transaction.create(request).send()Normalizes preparation, sending, waiting, fees, and receipt shape.
ethers.Contract / Anchor Programomnikit.contract(descriptor)Keeps ABI, IDL, and future contract systems behind adapters.
Why this matters
OmniKit gives product teams a single blockchain operating layer. That means fewer wallet-specific branches, fewer transaction edge cases in UI code, cleaner tests, smaller onboarding cost for new engineers, and a real path to supporting future chains without rewriting the app.