### Initialize Sui Client and Get System State (TypeScript) Source: https://context7.com/jovicheng/sui-zklogin-demo/llms.txt Sets up the Sui blockchain client using '@mysten/sui.js/client' and retrieves the current network's system state, specifically the epoch. It uses a predefined fullnode URL for the Devnet environment. The function logs detailed system state information and returns the current epoch number, which can then be used for further calculations like determining `maxEpoch`. ```typescript import { SuiClient } from "@mysten/sui.js/client"; const FULLNODE_URL = "https://fullnode.devnet.sui.io"; // Initialize Sui client const suiClient = new SuiClient({ url: FULLNODE_URL }); // Get current blockchain state async function getCurrentEpoch() { const systemState = await suiClient.getLatestSuiSystemState(); console.log("System State:", { epoch: systemState.epoch, protocolVersion: systemState.protocolVersion, systemStateVersion: systemState.systemStateVersion, storageFundTotalObjectStorageRebates: systemState.storageFundTotalObjectStorageRebates, storageFundNonRefundableBalance: systemState.storageFundNonRefundableBalance, }); // Output: // System State: { // epoch: "245", // protocolVersion: "48", // systemStateVersion: "1", // ... // } return systemState.epoch; } // Use epoch to calculate maxEpoch const currentEpoch = await getCurrentEpoch(); const maxEpoch = Number(currentEpoch) + 10; console.log(`Current Epoch: ${currentEpoch}, Max Epoch: ${maxEpoch}`); // Output: "Current Epoch: 245, Max Epoch: 255" ``` -------------------------------- ### Request Test Tokens from Sui Devnet Faucet using Axios Source: https://context7.com/jovicheng/sui-zklogin-demo/llms.txt Requests SUI test tokens from the Devnet faucet to fund a generated zkLogin address, enabling transaction execution. It uses `axios` to send a POST request to the faucet API. Includes error handling for rate limits and checks the balance using `@mysten/dapp-kit`. ```typescript import axios from "axios"; const SUI_DEVNET_FAUCET = "https://faucet.devnet.sui.io/gas"; async function requestFaucet(zkLoginUserAddress: string) { try { const response = await axios.post(SUI_DEVNET_FAUCET, { FixedAmountRequest: { recipient: zkLoginUserAddress, }, }); console.log("Faucet request successful:", response.data); // Output: { task: "abc123", transferred_gas_objects: [...] } // Wait a few seconds for transaction confirmation // Balance will update automatically via useSuiClientQuery } catch (error) { console.error("Faucet request failed:", error); // Common error: Rate limit exceeded // Alternative: Request via Discord #devnet-faucet channel } } // Check balance after faucet request import { useSuiClientQuery } from "@mysten/dapp-kit"; import { MIST_PER_SUI } from "@mysten/sui.js/utils"; const { data: addressBalance } = useSuiClientQuery( "getBalance", { owner: zkLoginUserAddress }, { enabled: Boolean(zkLoginUserAddress), refetchInterval: 1500 } ); if (addressBalance) { const balanceInSui = BigNumber(addressBalance.totalBalance) .div(MIST_PER_SUI.toString()) .toFixed(6); console.log(`Balance: ${balanceInSui} SUI`); // Output: "Balance: 1.000000 SUI" } ``` -------------------------------- ### Query Sui Address Balance with Real-time Updates (TypeScript) Source: https://context7.com/jovicheng/sui-zklogin-demo/llms.txt Monitors the balance of a zkLogin address in real-time using Sui's query hooks from '@mysten/dapp-kit'. It fetches balance data, converts it from MIST to SUI, and displays it. The query automatically refreshes every 1.5 seconds. It handles loading and error states gracefully. ```typescript import { useSuiClientQuery } from "@mysten/dapp-kit"; import { MIST_PER_SUI } from "@mysten/sui.js/utils"; import { BigNumber } from "bignumber.js"; function BalanceDisplay({ zkLoginUserAddress }: { zkLoginUserAddress: string }) { // Query balance with auto-refresh every 1.5 seconds const { data: addressBalance, isLoading, error } = useSuiClientQuery( "getBalance", { owner: zkLoginUserAddress, }, { enabled: Boolean(zkLoginUserAddress), // Only query when address exists refetchInterval: 1500, // Refresh every 1500ms } ); if (isLoading) return
Loading balance...
; if (error) return
Error loading balance: {error.message}
; if (addressBalance) { // Convert from MIST (smallest unit) to SUI const balanceInSui = BigNumber(addressBalance.totalBalance) .div(MIST_PER_SUI.toString()) .toFixed(6); return (

Address: {zkLoginUserAddress}

Balance: {balanceInSui} SUI

Total Balance (MIST): {addressBalance.totalBalance}

); } return
No balance data
; } ``` -------------------------------- ### Assemble zkLogin Signature and Execute Transaction with TypeScript Source: https://context7.com/jovicheng/sui-zklogin-demo/llms.txt Combines all necessary components to construct a valid zkLogin signature and subsequently executes a transaction on the Sui blockchain. This involves generating an address seed, signing the transaction with an ephemeral key, and finally executing the transaction block. Error handling is included for common issues like insufficient balance or invalid signatures. ```typescript import { TransactionBlock } from "@mysten/sui.js/transactions"; import { MIST_PER_SUI } from "@mysten/sui.js/utils"; import { genAddressSeed, getZkLoginSignature } from "@mysten/zklogin"; import { SerializedSignature } from "@mysten/sui.js/cryptography"; async function executeZkLoginTransaction() { // Create a transaction to transfer 1 SUI const txb = new TransactionBlock(); // Split 1 SUI from gas coin const [coin] = txb.splitCoins(txb.gas, [MIST_PER_SUI * 1n]); // Transfer to recipient address txb.transferObjects( [coin], "0xfa0f8542f256e669694624aa3ee7bfbde5af54641646a3a05924cf9e329a8a36" ); // Set sender as zkLogin address txb.setSender(zkLoginUserAddress); // Sign transaction with ephemeral key pair const { bytes, signature: userSignature } = await txb.sign({ client: suiClient, signer: ephemeralKeyPair, // Must be same key pair used in ZK proof request }); // Generate address seed from JWT claims and salt const addressSeed: string = genAddressSeed( BigInt(userSalt), "sub", // Key claim name decodedJwt.sub, // User's subject ID decodedJwt.aud as string // Application's client ID ).toString(); console.log("Address Seed:", addressSeed); // Assemble final zkLogin signature const zkLoginSignature: SerializedSignature = getZkLoginSignature({ inputs: { ...partialZkLoginSignature, // ZK proof from prover service addressSeed, // Derived address seed }, maxEpoch, userSignature, // Ephemeral key signature }); console.log("zkLogin Signature:", zkLoginSignature); // Execute transaction on Sui blockchain const executeRes = await suiClient.executeTransactionBlock({ transactionBlock: bytes, signature: zkLoginSignature, }); console.log("Transaction executed successfully!"); console.log("Digest:", executeRes.digest); console.log("Explorer:", `https://suiexplorer.com/txblock/${executeRes.digest}?network=devnet`); // Output: // Transaction executed successfully! // Digest: "8x7Y6z5W4v3U2t1S0r9Q8p7O6n5M4l3K2j1H0g9F8e7D6c5B4a3" // Explorer: "https://suiexplorer.com/txblock/8x7Y6z.../network=devnet" return executeRes; } // Execute with error handling try { const result = await executeZkLoginTransaction(); console.log("Success:", result); } catch (error) { console.error("Transaction failed:", error); // Common errors: // - Insufficient balance // - Expired ephemeral key (maxEpoch exceeded) // - Invalid signature // - Network issues } ``` -------------------------------- ### Fetch Zero-Knowledge Proof with TypeScript Source: https://context7.com/jovicheng/sui-zklogin-demo/llms.txt Requests a zero-knowledge proof from Mysten Labs' proving service. This is crucial for verifying JWT ownership without revealing sensitive credentials. It requires the JWT, ephemeral public key, maximum epoch, randomness, user salt, and the key claim name as input. The output is a partial zkLogin signature. ```typescript import axios from "axios"; import { getExtendedEphemeralPublicKey } from "@mysten/zklogin"; const SUI_PROVER_DEV_ENDPOINT = "https://prover-dev.mystenlabs.com/v1"; // First, get the extended ephemeral public key const extendedEphemeralPublicKey = getExtendedEphemeralPublicKey( ephemeralKeyPair.getPublicKey() ); console.log("Extended Public Key:", extendedEphemeralPublicKey); // Output: "AQNJ8K3L2M1P4R7T0W9Y5X6C8V1B2N4M7G3F9..." // Request ZK proof from Mysten Labs prover service const zkProofResult = await axios.post( SUI_PROVER_DEV_ENDPOINT, { jwt: idToken, // Full JWT from Google extendedEphemeralPublicKey: extendedEphemeralPublicKey, maxEpoch: maxEpoch, // Key validity period jwtRandomness: randomness, // Randomness used in nonce salt: userSalt, // User's salt value keyClaimName: "sub", // JWT claim to use (typically "sub") }, { headers: { "Content-Type": "application/json", }, } ); const partialZkLoginSignature = zkProofResult.data; console.log("ZK Proof received:", JSON.stringify(partialZkLoginSignature, null, 2)); // Output: // { // "proofPoints": { // "a": ["...", "..."], // "b": [["...", "..."], ["...", "..."]], // "c": ["...", "..."] // }, // "issBase64Details": { ... }, // "headerBase64": "..." // } ``` -------------------------------- ### Redirect to Google OAuth Login - TypeScript Source: https://context7.com/jovicheng/sui-zklogin-demo/llms.txt Constructs the OAuth2 URL with the generated nonce and redirects the user to Google's authentication page for login. It specifies the client ID, redirect URI, response type (id_token), and scope (openid) to facilitate the OAuth flow. ```typescript const CLIENT_ID = "573120070871-0k7ga6ns79ie0jpg1ei6ip5vje2ostt6.apps.googleusercontent.com"; const REDIRECT_URI = "https://sui-zklogin.vercel.app/"; // Build OAuth URL with required parameters const params = new URLSearchParams({ client_id: CLIENT_ID, redirect_uri: REDIRECT_URI, response_type: "id_token", // Request JWT as id_token scope: "openid", // OpenID Connect scope nonce: nonce, // Previously generated nonce }); const loginURL = `https://accounts.google.com/o/oauth2/v2/auth?${params}`; window.location.replace(loginURL); // After successful authentication, Google redirects back with JWT: // https://sui-zklogin.vercel.app/#id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6... ``` -------------------------------- ### Generate User Salt with @mysten/zklogin Source: https://context7.com/jovicheng/sui-zklogin-demo/llms.txt Creates a unique and persistent salt value for each user, ensuring deterministic address derivation across sessions. This salt is stored in `localStorage` for client-side persistence. The `generateRandomness` function from `@mysten/zklogin` is used. ```typescript import { generateRandomness } from "@mysten/zklogin"; // Generate user salt (stores in localStorage for persistence) const userSalt = generateRandomness(); window.localStorage.setItem("demo_user_salt_key_pair", userSalt); console.log("User Salt:", userSalt); // Output: "128318743981273498123749812374981273498" // Retrieve salt in future sessions const retrievedSalt = window.localStorage.getItem("demo_user_salt_key_pair"); // IMPORTANT: The same JWT (Google account) + same salt = same Sui address // Different salt = different address, even with same Google account // Losing the salt means losing access to the previously generated address // Salt storage options: // 1. User responsibility: Send to user's email // 2. Client-side: Store in browser localStorage (as shown) // 3. Backend database: Map UID to salt for enterprise applications ``` -------------------------------- ### Generate User Sui Address with @mysten/zklogin Source: https://context7.com/jovicheng/sui-zklogin-demo/llms.txt Derives a deterministic Sui blockchain address using the decoded JWT claims and the user's unique salt. This function, `jwtToAddress` from `@mysten/zklogin`, ensures that the same inputs always produce the same address, enabling account recovery without private key management. ```typescript import { jwtToAddress } from "@mysten/zklogin"; // Generate zkLogin Sui address from JWT and salt const zkLoginUserAddress = jwtToAddress(idToken, userSalt); console.log("zkLogin Sui Address:", zkLoginUserAddress); // Output: "0x1a2b3c4d5e6f7890abcdef1234567890abcdef1234567890abcdef1234567890" // Address is deterministically derived from: // - JWT.sub (subject): User's unique ID from OAuth provider // - JWT.iss (issuer): OAuth provider URL // - JWT.aud (audience): Your application's client ID // - userSalt: Your application's salt value // The same inputs ALWAYS produce the same address // This enables account recovery without private key storage ``` -------------------------------- ### Generate Nonce for OAuth Authentication - TypeScript Source: https://context7.com/jovicheng/sui-zklogin-demo/llms.txt Generates a cryptographic nonce that links the OAuth JWT with the ephemeral key pair. This nonce is essential for verifying zero-knowledge proofs. It calculates the maximum epoch for key validity and generates randomness before creating the nonce. ```typescript import { generateRandomness, generateNonce } from "@mysten/zklogin"; import { SuiClient } from "@mysten/sui.js/client"; const suiClient = new SuiClient({ url: "https://fullnode.devnet.sui.io" }); // Get current epoch and calculate maxEpoch (key validity period) const { epoch } = await suiClient.getLatestSuiSystemState(); const currentEpoch = Number(epoch); const maxEpoch = currentEpoch + 10; // Valid for 10 epochs (~10 days) // Generate randomness for nonce const randomness = generateRandomness(); window.sessionStorage.setItem("demo_randomness_key_pair", randomness); // Generate nonce using ephemeralKeyPair, maxEpoch, and randomness const nonce = generateNonce( ephemeralKeyPair.getPublicKey(), maxEpoch, randomness ); console.log("Generated nonce:", nonce); ``` -------------------------------- ### Decode JWT Token with jwt-decode Source: https://context7.com/jovicheng/sui-zklogin-demo/llms.txt Parses and validates a JWT token received from an OAuth provider (e.g., Google). It extracts user claims such as issuer, audience, and subject, which are essential for generating a deterministic Sui address. Dependencies include `jwt-decode` and `query-string`. ```typescript import { JwtPayload, jwtDecode } from "jwt-decode"; import queryString from "query-string"; // Parse OAuth callback URL const parsed = queryString.parse(window.location.hash); const idToken = parsed.id_token as string; // Decode JWT to get payload const decodedJwt = jwtDecode(idToken) as JwtPayload; console.log("JWT Payload:", JSON.stringify(decodedJwt, null, 2)); // Example output: // { // "iss": "https://accounts.google.com", // "aud": "573120070871-0k7ga6ns79ie0jpg1ei6ip5vje2ostt6.apps.googleusercontent.com", // "sub": "106294929772244112345", // Unique user identifier // "email": "user@example.com", // "nonce": "hNj9q8KZe3L2m1P4r7T0w9Y5x6C8v1B2n4M7g3F9k2J5h8L1", // "nbf": 1698765432, // "iat": 1698765432, // "exp": 1698769032, // "jti": "abc123def456" // } // Key fields for zkLogin: // - iss: OAuth provider (issuer) // - aud: Your application's client ID (audience) // - sub: User's unique identifier (subject) // - nonce: Must match the generated nonce ``` -------------------------------- ### Reset Local Authentication State (TypeScript) Source: https://context7.com/jovicheng/sui-zklogin-demo/llms.txt Clears all stored authentication data from both sessionStorage and localStorage, and resets various application state variables. This function effectively returns the application to its initial state. It includes a warning about the irretrievability of addresses after clearing localStorage due to the removal of the user salt. ```typescript function resetLocalState() { try { // Clear all sessionStorage (ephemeral data) window.sessionStorage.clear(); // Removes: ephemeralKeyPair, randomness // Clear all localStorage (persistent data) window.localStorage.clear(); // Removes: userSalt, maxEpoch // WARNING: Clearing localStorage removes the user salt // This makes previously generated addresses irretrievable! // Reset application state setCurrentEpoch(""); setNonce(""); setOauthParams(undefined); setZkLoginUserAddress(""); setDecodedJwt(undefined); setJwtString(""); setEphemeralKeyPair(undefined); setUserSalt(undefined); setZkProof(undefined); setExtendedEphemeralPublicKey(""); setMaxEpoch(0); setRandomness(""); setActiveStep(0); setExecuteDigest(""); // Navigate to home page navigate("/"); console.log("Local state reset successful"); } catch (error) { console.error("Failed to reset local state:", error); } } ``` -------------------------------- ### Generate Ephemeral Key Pair - TypeScript Source: https://context7.com/jovicheng/sui-zklogin-demo/llms.txt Generates a temporary Ed25519 key pair required for signing transaction blocks within the zkLogin flow. The generated key pair is stored in sessionStorage for subsequent retrieval and use. This function is crucial for establishing a temporary identity for the user during the authentication process. ```typescript import { Ed25519Keypair } from "@mysten/sui.js/keypairs/ed25519"; // Generate a new ephemeral key pair const ephemeralKeyPair = Ed25519Keypair.generate(); // Store in sessionStorage (cleared when browser closes) window.sessionStorage.setItem( "demo_ephemeral_key_pair", ephemeralKeyPair.export().privateKey ); // Retrieve the key pair later const privateKey = window.sessionStorage.getItem("demo_ephemeral_key_pair"); if (privateKey) { const restoredKeyPair = Ed25519Keypair.fromSecretKey(fromB64(privateKey)); console.log("Public Key:", restoredKeyPair.getPublicKey().toBase64()); } ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.