Cross-chain Web3 connection SDK. Pure library, no frontend components.
connect-lib/ is a framework-agnostic library monorepo that only handles connection logic. Frontend components (React hooks, UI components) live in the separate connect-react/ project.
naculus/
├── connect-lib/ ← You are here: pure library
└── connect-react/ ← Frontend components (React)
| Package | Description | Dependencies |
|---|---|---|
@naculus/connect-core |
Core interfaces: UniversalConnector, UniversalWalletSession, ConnectorManager, WalletError |
— |
@naculus/connector-walletconnect |
WalletConnect v2 bridge | core |
@naculus/connector-evm-injected |
EIP-6963 browser-injected wallet discovery | core |
@naculus/connector-embedded |
Embedded non-custodial wallet (implemented via wallet-engine) | core + wallet-engine |
@naculus/connector-passkeys |
WebAuthn Passkeys wallet | core |
@naculus/connector-solana |
Solana injected wallet (Phantom / Solflare) | core |
@naculus/connector-xrpl |
XRP Ledger wallet (Xaman) | core |
@naculus/wallet-engine |
Low-level crypto engine (BIP39, HD derivation, signing) | @noble/* |
@naculus/siwx |
CAIP-122 Sign-In With X cross-chain verification | core |
wallet-engine (crypto engine)
│
v
connector-embedded
│
v
core ← connector-walletconnect / connector-evm-injected / connector-passkeys / connector-solana / connector-xrpl
│
v
siwx
⚠️ 0.x — the API can still change between minor versions. Published on npm; see Releasing for how versions move.
pnpm add @naculus/connect
# Or individual packages: pnpm add @naculus/connect-core @naculus/connector-walletconnectUse React? Install
connect-reactinstead — it wraps the library in hooks. See connect-react.
import { ConnectorManager } from "@naculus/connect-core";
import { WalletConnectConnector, createWalletConnectConnector } from "@naculus/connector-walletconnect";
const manager = new ConnectorManager();
// Register connector
const wcConnector = createWalletConnectConnector({
projectId: "your_project_id",
metadata: { name: "My App", description: "...", url: "...", icons: [] },
});
manager.register("walletconnect", wcConnector);
// Connect
const session = await manager.connect("walletconnect");
// Sign
const signature = await manager.signMessage({ message: "hello" });
// Disconnect
await manager.disconnect();| Standard | Status | Package |
|---|---|---|
| EIP-1193 (Provider API) | ✅ | connector-evm-injected |
| EIP-6963 (Multi Injected Provider Discovery) | ✅ | connector-evm-injected |
| EIP-4361 / CAIP-122 (SIWx) | ✅ | siwx |
| ERC-1271 (contract-account signatures) | ✅ | siwx |
| ERC-6492 (pre-deployment signatures) | ✅ | siwx |
| EIP-5792 (wallet calls) | ✅ | core, connector-evm-injected, connector-walletconnect, connector-coinbase |
| Solana Wallet Standard | ✅ | connector-solana |
| WalletConnect v2 (CAIP-25) | ✅ | connector-walletconnect |
All four methods are implemented: wallet_getCapabilities,
wallet_sendCalls, wallet_getCallsStatus and wallet_showCallsStatus.
The point of the capability query is that an application decides how to execute before it sends anything, rather than learning the answer from a rejection:
const { atomic, current } = useCapabilities(); // @naculus/connect-appkit-react
if (atomic === "supported") {
await sendCalls(calls); // one batch, all or nothing
} else {
// Sequential. An approve can land and the swap it was for can fail, so an
// application that needs all-or-nothing should stop here instead.
}atomic has three values, not two. A wallet that does not implement
wallet_getCapabilities has not said no — it has said nothing, and EIP-5792 is
explicit that absence is not a denial. "unknown" also covers a query that
failed and one still in flight.
The wire format is decoded in one place (core/src/eip5792.ts) because it
changed between the draft and 2.0.0, and three connectors each decoded it
inline and each got it wrong differently: one invented a "no" for a wallet that
was never asked, one read Boolean({ supported: false }) as true, one accepted
status: "supported" and dropped "ready".
Guides:
- Upgrading from 0.10 to 0.11 — the CCTP V2 bridge for native USDC (
@naculus/connect-core/cctp) and session-key error messages that now end with the reason (match oncode). - Upgrading from 0.7 to 0.10 — what changed in 0.8.0–0.10.0 and the one action needed (hand-built
eip7702scopes). - Paying x402 from an external wallet — MetaMask, Coinbase Wallet, Rabby, OKX or Phantom with
walletX402Signer+createX402Fetch, thenverifyX402Settlement. - Charging with x402 on your server —
requirePayment+settlePayment, settled with viem. - Periodic authorization — per-period spending limits, enforced by the device or on chain.
Reference: docs overview, architecture, design notes.
pnpm install
pnpm build # Build all packages
pnpm test:run # Run tests
pnpm tsc --noEmit # Type checkpnpm --filter @naculus/connect-core build
pnpm --filter @naculus/connector-solana testpnpm tunnel # Start tunnel (default port 3000)
pnpm tunnel:3000 # Specify portconnect-lib/
├── packages/ # Main packages
│ ├── core/ # Core interfaces and types
│ ├── connector-*/ # Per-chain connectors
│ ├── wallet-engine/ # Crypto engine
│ └── siwx/ # Cross-chain identity verification
├── e2e/ # E2E tests
├── scripts/ # Dev helper scripts
└── docs/ # Technical documentation
Q: Does connect-lib/ have UI components?
No. Frontend components are in connect-react/.
Q: What's the difference between wallet-engine and connector-embedded?
wallet-engine is a pure crypto library (key generation, signing) — it doesn't know what UniversalConnector is. connector-embedded is the adapter layer that wraps wallet-engine into a connector.
Q: How to add a new connector?
Implement the UniversalConnector interface (packages/core/src/connector.ts), then register it in ConnectorManager.