zkLogin Wallets
zkLogin wallets derive a Sui address from an OAuth credential rather than a traditional private key or recovery passphrase. The user signs in with a provider they already use (Google, Apple, Twitch, and others), and the wallet generates a Sui address tied to that credential. No seed phrase is created, and no persistent private key is stored by the OAuth provider. zkLogin is a primitive native to Sui, designed to remove the key management burden for users who are new to onchain applications.
How zkLogin wallets work
At a high level, a zkLogin wallet works as follows:
- The app generates an ephemeral key pair, valid for a limited number of epochs.
- The user authenticates with an OAuth provider. The provider returns a JSON Web Token (JWT) that contains a nonce derived from the ephemeral public key.
- The app or a proving service uses the JWT to generate a zero-knowledge proof. The proof confirms the user holds a valid OAuth credential without revealing the credential onchain.
- The app uses the JWT, a per-user salt, and the issuer URL to derive a stable Sui address for the user. The same credential always produces the same address for a given app and salt.
- Transactions are signed with the ephemeral private key and submitted alongside the zero-knowledge proof. Validators verify the proof and execute the transaction.
Because zkLogin is a two-factor scheme, an attacker who compromises an OAuth account cannot sign transactions unless they also compromise the per-user salt.
The per-user salt must be persisted and recoverable: if a user loses their salt, they permanently lose access to the derived address, even with a valid OAuth login. Store and back up salts reliably, and never log or expose JWTs or zero-knowledge proofs, as these are sensitive credentials. Manage the ephemeral key pair's max epoch and session lifetime carefully so sessions expire as intended. See Security Best Practices.
Enoki
Enoki is a Mysten Labs platform that wraps zkLogin and sponsored transactions behind a straightforward API. Rather than managing proof generation, salt storage, and OAuth configuration yourself, you register your app on the Enoki Developer Portal, configure your OAuth providers, and use the @mysten/enoki SDK to handle the rest.
Enoki implements the Wallet Standard and integrates with Sui dApp Kit through registerEnokiWallets. Once registered, they appear in the standard connection UI alongside any other installed wallets.
packages/enoki/src/wallet/register.ts. You probably need to run `pnpm prebuild` and restart the site.Wallet Standard account fields
When using Enoki with @mystenlabs/dapp-kit, the useCurrentAccount hook returns a Wallet Standard account populated by the Enoki wallet. The account exposes address, chains, features, publicKey, and icon (the standard fields defined by the Wallet Standard, not OAuth-specific identity fields).
import { useCurrentAccount } from '@mysten/dapp-kit-react';
function UserProfile() {
const account = useCurrentAccount();
if (!account) return null;
return (
<div>
{/* The provider icon registered by the Enoki wallet */}
{account.icon && <img src={account.icon} alt="Provider" />}
{/* The Sui address derived from zkLogin */}
<span>{account.address}</span>
</div>
);
}
The Wallet Standard account does not expose OAuth identity fields like email or sub. If your app needs the user's email, read it from the JWT claims during the OAuth login flow (request the email scope) and store it in your application. zkLogin does not expose identity information onchain or through the wallet interface. See Can my app retrieve a user's email?.
For server-side identity resolution, refer to the Enoki documentation.
Playtron wallet
The Playtron wallet is the default zkLogin wallet on the SuiPlay0X1. Every SuiPlay0X1 user has a Playtron account, and every Playtron account has an associated zkLogin wallet derived from those credentials.
Games running on the SuiPlay0X1 must support the Playtron wallet as the default option. Off-device versions of those games should use Sui dApp Kit to allow users to connect their Playtron wallet through a web interface.
zkLogin SDK
The @mysten/sui/zklogin module in the Sui TypeScript SDK provides utilities for building zkLogin wallets and apps directly, without using a managed service like Enoki. Use this SDK when you need full control over proof generation, salt management, and address derivation.
Install the Sui TypeScript SDK:
npm i @mysten/sui
Core utilities
All zkLogin utilities are exported from @mysten/sui/zklogin.
Derive a Sui address from a JWT:
packages/sui/src/zklogin/address.ts. You probably need to run `pnpm prebuild` and restart the site.Derive an address from a parsed JWT:
packages/sui/src/zklogin/address.ts. You probably need to run `pnpm prebuild` and restart the site.Derive an address from an address seed:
packages/sui/src/zklogin/address.ts. You probably need to run `pnpm prebuild` and restart the site.Serialize a zkLogin signature for transaction submission:
packages/sui/src/zklogin/signature.ts. You probably need to run `pnpm prebuild` and restart the site.Parse an existing serialized zkLogin signature:
packages/sui/src/zklogin/signature.ts. You probably need to run `pnpm prebuild` and restart the site.ZkLoginSigner
The ZkLoginSigner class wraps an ephemeral signer and automatically transforms every signature into a zkLogin signature. Instead of manually calling getZkLoginSignature after each sign, you create a ZkLoginSigner once and use it like any other signer. It handles the proof wrapping internally.
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
import { ZkLoginSigner } from '@mysten/sui/zklogin';
const zkSigner = new ZkLoginSigner({
ephemeralSigner: ephemeralKeyPair,
maxEpoch: maxEpoch,
inputs: zkLoginInputs, // ZkLoginSignatureInputs including addressSeed
legacyAddress: false, // true only for addresses derived before the fix
address: zkLoginUserAddress, // optional: validates derived address matches
client: suiClient, // optional: enables signature verification
});
// Use it like any signer — zkLogin wrapping happens automatically
const { bytes, signature } = await zkSigner.signTransaction(txBytes);
await client.executeTransaction({ transaction: bytes, signatures: [signature] });
Key behavior:
getKeyScheme()returns'ZkLogin', distinguishing it from Ed25519, Secp256k1, and other scheme signers.getPublicKey()returns aZkLoginPublicIdentifierderived from the proof inputs and issuer.legacyAddressflag controls which address derivation is used. An early implementation had an inconsistency; if your addresses were derived before the fix, passtrue. For new integrations, usefalse.address(optional) validates that the derived address matches what you expect. The constructor throws if there's a mismatch, catchinglegacyAddressflag errors early.sign()throws. UsesignTransaction()orsignPersonalMessage()instead, because zkLogin signatures require intent context.
Proof generation
The Sui TypeScript SDK handles address derivation and signature serialization, but it does not generate zero-knowledge proofs. Proof generation requires a prover service:
- Mysten Labs prover: A publicly accessible proving service maintained by Mysten Labs. Suitable for Testnet and Devnet development. See the zkLogin integration guide for the endpoint and request format.
- Self-hosted prover: You run your own prover for production environments where you need full control over the proving infrastructure.