One Passkey, Multiple Chains - Cross-chain passkey authentication for Web3.
Build passwordless blockchain applications using WebAuthn passkeys. Users authenticate with biometrics (Face ID, Touch ID, Windows Hello) instead of managing seed phrases.
- Passwordless: No seed phrases, no private keys to manage
- Cross-Chain: Single passkey works across Solana, Sui, and more
- Secure: Hardware-backed WebAuthn standard
- Developer-Friendly: Simple React hooks, TypeScript support
- Portal-Based: Custom signing UI with full UX control
- Lightweight: Minimal dependencies, tree-shakeable
This monorepo contains the following packages:
| Package | Version | Description |
|---|---|---|
| @zero-lync/passkey-core | 0.1.0 | Core utilities and types |
| @zero-lync/passkey-solana | 0.1.0 | Solana passkey authentication |
| @zero-lync/passkey-sui | 0.1.0 | Sui passkey authentication |
| Package | Description |
|---|---|
| Portal | Signing portal service with custom UI |
| App | Description |
|---|---|
| Demo | Cross-chain wallet demo application |
# Install Solana SDK
npm install @zero-lync/passkey-solana @solana/web3.js
# Install Sui SDK
npm install @zero-lync/passkey-sui @mysten/sui
# Or install both for cross-chain support
npm install @zero-lync/passkey-solana @zero-lync/passkey-sui @solana/web3.js @mysten/suiimport { SolanaPasskeyProvider, useSolanaPasskey } from '@zero-lync/passkey-solana';
import { SystemProgram, PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js';
function App() {
return (
<SolanaPasskeyProvider
rpcUrl="https://api.devnet.solana.com"
portalUrl="https://your-portal-url.com"
>
<Wallet />
</SolanaPasskeyProvider>
);
}
function Wallet() {
const { address, connect, signAndSendTransaction } = useSolanaPasskey();
const handleTransfer = async () => {
const instruction = SystemProgram.transfer({
fromPubkey: new PublicKey(address!),
toPubkey: new PublicKey('recipient-address'),
lamports: 0.1 * LAMPORTS_PER_SOL,
});
await (instruction);
};
return (
<div>
{!address ? (
<button onClick={connect}>Connect Wallet</button>
) : (
<>
<p>Address: {address}</p>
<button onClick={handleTransfer}>Send 0.1 SOL</button>
</>
)}
</div>
);
}import { SuiPasskeyProvider, useSuiPasskey } from '@zero-lync/passkey-sui';
import { Transaction } from '@mysten/sui/transactions';
function App() {
return (
<SuiPasskeyProvider
network="devnet"
portalUrl="https://your-portal-url.com"
>
<Wallet />
</SuiPasskeyProvider>
);
}
function Wallet() {
const { address, connect, signAndExecuteTransaction } = useSuiPasskey();
const handleTransfer = async () => {
const tx = new Transaction();
const [coin] = tx.splitCoins(tx.gas, [0.1 * 1e9]);
tx.transferObjects([coin], 'recipient-address');
await signAndExecuteTransaction(tx);
};
return (
<div>
{!address ? (
<button onClick={connect}>Connect Wallet</button>
) : (
<>
<p>Address: {address}</p>
<button onClick={handleTransfer}>Send 0.1 SUI</button>
</>
)}
</div>
);
}import { SolanaPasskeyProvider, useSolanaPasskey } from '@zero-lync/passkey-solana';
import { SuiPasskeyProvider, useSuiPasskey } from '@zero-lync/passkey-sui';
function App() {
return (
<SolanaPasskeyProvider
rpcUrl="https://api.devnet.solana.com"
portalUrl="https://your-portal-url.com"
>
<SuiPasskeyProvider
network="devnet"
portalUrl="https://your-portal-url.com"
>
<CrossChainWallet />
</SuiPasskeyProvider>
</SolanaPasskeyProvider>
);
}
function CrossChainWallet() {
const solana = useSolanaPasskey();
const sui = useSuiPasskey();
const connectBoth = async () => {
// Connect to Solana first (creates passkey)
await solana.connect();
// Connect to Sui (reuses same passkey)
await sui.connect();
};
return (
<div>
<button onClick={connectBoth}>Connect to Both Chains</button>
{solana.address && <p>Solana: {solana.address}</p>}
{sui.address && <p>Sui: {sui.address}</p>}
</div>
);
}This project uses pnpm workspaces for monorepo management.
- Node.js >= 20
- pnpm >= 9.0.0
# Clone the repository
git clone https://github.com/zerolync/zerolync-passkey-sdk.git
cd zerolync-passkey-sdk
# Install dependencies
pnpm install
# Build all packages
pnpm build
# Run demo app
pnpm devzerolync-passkey-sdk/
├── sdk/ # SDK packages
│ ├── core/ # Core utilities
│ ├── solana/ # Solana SDK
│ └── sui/ # Sui SDK
├── services/ # Backend services
│ └── portal/ # Signing portal
├── apps/ # Applications
│ └── demo/ # Demo app
├── package.json # Root package.json
└── pnpm-workspace.yaml # Workspace configuration
# Build all packages
pnpm build
# Build specific package
pnpm --filter @zero-lync/passkey-solana build
# Watch mode for development
pnpm --filter @zero-lync/passkey-solana devThe demo app showcases cross-chain wallet functionality:
# Development mode (HTTP)
pnpm dev
# Development mode (HTTPS)
pnpm --filter demo dev:httpsVisit http://localhost:3000
The portal service provides the signing interface:
cd services/portal
pnpm devVisit http://localhost:5173
The SDKs are published to npm under the @zero-lync scope.
# From the package directory
cd sdk/solana
pnpm publish
# Or from root
pnpm --filter @zero-lync/passkey-solana publishThe prepublishOnly script will automatically build the package before publishing.
Update versions in the respective package.json files before publishing. We follow semantic versioning:
- Patch (0.1.x): Bug fixes
- Minor (0.x.0): New features, backward compatible
- Major (x.0.0): Breaking changes
Create a .env.local file in apps/demo/:
NEXT_PUBLIC_SOLANA_RPC_URL=https://api.devnet.solana.com
NEXT_PUBLIC_SUI_RPC_URL=https://fullnode.testnet.sui.io:443
NEXT_PUBLIC_PORTAL_URL=http://localhost:5173
NEXT_PUBLIC_PAYMASTER_URL=https://your-paymaster.com # optionalCreate a .env file in services/portal/ if needed for custom configuration.
- Passkey Creation: When a user connects for the first time, a WebAuthn passkey is created using their device's biometric authentication
- Keypair Derivation: The passkey's public key is used to derive blockchain-specific keypairs (Solana, Sui, etc.)
- Address Generation: Each blockchain address is generated from its respective keypair
- Signing: Transactions are signed using the WebAuthn credential through the portal interface
- Cross-Chain: The same passkey can be reused across different blockchains
- Hardware-Backed: Passkeys are stored in secure hardware (TPM, Secure Enclave)
- No Seed Phrases: Private keys never leave the device
- WebAuthn Standard: Uses the W3C WebAuthn specification
- Origin-Bound: Passkeys are bound to your domain for phishing protection
- ✅ Chrome/Edge 67+
- ✅ Safari 16+
- ✅ Firefox 60+
- ✅ Mobile browsers (iOS Safari, Chrome Android)
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT - see LICENSE for details
- GitHub Issues: Report a bug
- Discord: Join our community
- Twitter: @zerolync
Made with ❤️ by the Zerolync team