npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dulcetlabs/suglink

v0.3.0

Published

Social login with multi-chain wallet creation (Solana & Sui) - Non-custodial Web3 onboarding made simple

Readme

SugLink

DULCET LABS INTERNAL PACKAGE

⚠️ IMPORTANT: PERMISSION REQUIRED FOR USE ⚠️

This software requires explicit written permission from Dulcet Labs before any commercial use, including but not limited to production deployment, incorporation into commercial products, or use by competing businesses.

SugLink is an internal Dulcet Labs library that enables easy onboarding and multi-chain wallet creation using existing social accounts (Google, TikTok, and Snapchat). It provides a seamless way to generate deterministic keypairs for Solana and Sui blockchains based on social accounts.

Features

  • Multi-Chain Support: Generate wallets for Solana and Sui blockchains from the same social login
  • App-Specific Wallet Isolation: Each app gets unique wallets for the same user via appId parameter
  • Chain Selection: Choose "solana", "sui", or "all" chains during login
  • Universal Compatibility: Works seamlessly in both Node.js and browser environments using Web Crypto API and native crypto modules
  • Social Login Integration: Full Google OAuth support, with TikTok and Snapchat in development
  • Deterministic Keypair Generation: Generates blockchain keypairs based on social credentials using universal crypto implementations
  • Backward Compatibility: Existing Solana-only implementations continue to work without changes
  • Secure OAuth Flow Handling: Manages OAuth securely with CSRF protection and environment variable support
  • TypeScript Support: Includes TypeScript definitions for better development experience
  • Dual Module Support: Compatible with both ESM and CommonJS
  • Browser Ready: Automatically detects environment and uses appropriate crypto APIs (Web Crypto API in browsers, Node.js crypto in server)

Universal Compatibility

SugLink works in all JavaScript environments:

Node.js Applications

  • Uses native Node.js crypto module for maximum performance
  • Environment variables supported via dotenv
  • Full OAuth server implementation

Browser Applications

  • Uses Web Crypto API for secure operations
  • Works with React, Vue, Angular, vanilla JavaScript
  • Compatible with all modern bundlers (Webpack, Vite, Rollup)
  • No Node.js dependencies in browser builds

React Native

  • Universal crypto implementation ensures compatibility
  • Same API across all platforms

🔧 Environment Detection

SugLink automatically detects the runtime environment and uses the appropriate crypto implementation:

  • Browsers: Web Crypto API (window.crypto.subtle)
  • Node.js: Native crypto module (require('crypto'))
  • Buffer: Universal Buffer polyfill for browser compatibility

Installation

Install the package using npm, yarn, or pnpm:

# Using npm
npm install @dulcetlabs/suglink @solana/web3.js

# For Sui support (optional)
npm install @mysten/sui

# Using yarn
yarn add @dulcetlabs/suglink @solana/web3.js @mysten/sui

# Using pnpm
pnpm add @dulcetlabs/suglink @solana/web3.js @mysten/sui

Browser Setup Notes

For browser applications, modern bundlers automatically handle the universal compatibility:

  • Webpack: Automatically polyfills Node.js modules
  • Vite: Built-in support for universal packages
  • Create React App: Works out of the box
  • Next.js: Full compatibility in both SSR and client-side

No additional configuration required! SugLink automatically detects the browser environment and uses Web Crypto API.

Setup

Environment Variables

Create a .env file in your project root and add your OAuth credentials:

# Google OAuth Configuration
GOOGLE_CLIENT_ID=your_google_client_id
GOOGLE_CLIENT_SECRET=your_google_client_secret
GOOGLE_REDIRECT_URI=your_redirect_uri

# TikTok OAuth Configuration
TIKTOK_CLIENT_ID=your_tiktok_client_id
TIKTOK_CLIENT_SECRET=your_tiktok_client_secret
TIKTOK_REDIRECT_URI=your_redirect_uri

# Snapchat OAuth Configuration
SNAPCHAT_CLIENT_ID=your_snapchat_client_id
SNAPCHAT_CLIENT_SECRET=your_snapchat_client_secret
SNAPCHAT_REDIRECT_URI=your_redirect_uri

Loading Environment Variables

Ensure that the dotenv package is used to load these variables:

import * as dotenv from "dotenv";

// Load environment variables from .env file
dotenv.config();

Usage

React Application Example

import React, { useState, useEffect } from "react";
import { SugLink } from "@dulcetlabs/suglink";

const App = () => {
  const [publicKey, setPublicKey] = useState<string | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const sugLink = new SugLink();

  // Handle OAuth callback
  useEffect(() => {
    const handleCallback = async () => {
      const urlParams = new URLSearchParams(window.location.search);
      const code = urlParams.get("code");
      const state = urlParams.get("state");
      const expectedState = localStorage.getItem("oauth_state");

      if (code && state && expectedState) {
        try {
          setIsLoading(true);
          await sugLink.handleCallback("google", code, state, expectedState);
          setPublicKey(sugLink.getPublicKey());
          localStorage.removeItem("oauth_state");
          // Clean up URL
          window.history.replaceState(
            {},
            document.title,
            window.location.pathname
          );
        } catch (error) {
          console.error("Login failed:", error);
        } finally {
          setIsLoading(false);
        }
      }
    };

    handleCallback();
  }, []);

  const handleLogin = async (platform: "google") => {
    try {
      const state = await sugLink.generateState();
      localStorage.setItem("oauth_state", state);
      const authUrl = sugLink.getAuthUrl(platform, state);
      window.location.href = authUrl;
    } catch (error) {
      console.error("Failed to initiate login:", error);
    }
  };

  const handleLogout = () => {
    sugLink.logout();
    setPublicKey(null);
  };

  if (isLoading) return <div>Logging in...</div>;

  return (
    <div>
      {!publicKey ? (
        <button onClick={() => handleLogin("google")}>Login with Google</button>
      ) : (
        <div>
          <p>Public Key: {publicKey}</p>
          <button onClick={handleLogout}>Logout</button>
        </div>
      )}
    </div>
  );
};

export default App;

Multi-Chain Support Example

import React, { useState } from "react";
import { SugLink } from "@dulcetlabs/suglink";
import { Keypair } from "@solana/web3.js";

const MultiChainApp = () => {
  const [wallets, setWallets] = useState<{
    solana?: { publicKey: string; privateKey: Uint8Array };
    sui?: { address: string; privateKey: string };
  }>({});
  const [selectedChain, setSelectedChain] = useState<"solana" | "sui" | "all">(
    "all"
  );
  const sugLink = new SugLink();

  const handleMultiChainLogin = async () => {
    try {
      const urlParams = new URLSearchParams(window.location.search);
      const code = urlParams.get("code");

      if (!code) {
        // Redirect to OAuth
        const state = await sugLink.generateState();
        localStorage.setItem("oauth_state", state);
        const authUrl = sugLink.getAuthUrl("google", state);
        window.location.href = authUrl;
        return;
      }

      // Handle OAuth callback with multi-chain support
      const result = await sugLink.login("google", code, selectedChain);

      if (typeof result === "object" && "solana" in result) {
        // Multi-chain result
        const walletData: typeof wallets = {};

        if (result.solana) {
          walletData.solana = {
            publicKey: result.solana.publicKey,
            privateKey: result.solana.privateKey,
          };
        }

        if (result.sui) {
          walletData.sui = {
            address: result.sui.address,
            privateKey: result.sui.privateKey,
          };
        }

        setWallets(walletData);
      } else {
        // Legacy Solana-only result
        const keypair = result as Keypair;
        setWallets({
          solana: {
            publicKey: keypair.publicKey.toString(),
            privateKey: keypair.secretKey,
          },
        });
      }
    } catch (error) {
      console.error("Multi-chain login failed:", error);
    }
  };

  return (
    <div>
      <h2>Multi-Chain Wallet Generator</h2>

      <div>
        <label>
          <input
            type="radio"
            value="solana"
            checked={selectedChain === "solana"}
            onChange={(e) => setSelectedChain(e.target.value as "solana")}
          />
          Solana Only
        </label>
        <label>
          <input
            type="radio"
            value="sui"
            checked={selectedChain === "sui"}
            onChange={(e) => setSelectedChain(e.target.value as "sui")}
          />
          Sui Only
        </label>
        <label>
          <input
            type="radio"
            value="all"
            checked={selectedChain === "all"}
            onChange={(e) => setSelectedChain(e.target.value as "all")}
          />
          Both Chains
        </label>
      </div>

      <button onClick={handleMultiChainLogin}>
        Generate{" "}
        {selectedChain === "all" ? "Multi-Chain" : selectedChain.toUpperCase()}{" "}
        Wallet
      </button>

      {wallets.solana && (
        <div>
          <h3>Solana Wallet</h3>
          <p>
            <strong>Public Key:</strong> {wallets.solana.publicKey}
          </p>
          <p>
            <strong>Private Key:</strong> {wallets.solana.privateKey.length}{" "}
            bytes
          </p>
        </div>
      )}

      {wallets.sui && (
        <div>
          <h3>Sui Wallet</h3>
          <p>
            <strong>Address:</strong> {wallets.sui.address}
          </p>
          <p>
            <strong>Private Key:</strong> {wallets.sui.privateKey}
          </p>
        </div>
      )}
    </div>
  );
};

export default MultiChainApp;

App-Specific Wallet Isolation

Problem: By default, the same user gets the same wallet across all apps using SugLink, which creates security and UX issues.

Solution: Use the appId parameter to generate unique wallets per app while maintaining deterministic wallets within each app.

import { SugLink } from "@dulcetlabs/suglink";

const sugLink = new SugLink();

// Each app should have a unique identifier
const ecommerceConfig = {
  clientId: "your-google-client-id",
  clientSecret: "your-google-client-secret", 
  redirectUri: "http://localhost:3000/callback",
  appId: "ecommerce-platform-v1" // 🔑 Unique app identifier
};

const gamingConfig = {
  clientId: "your-google-client-id",
  clientSecret: "your-google-client-secret",
  redirectUri: "http://localhost:3000/callback", 
  appId: "gaming-platform-v1" // 🔑 Different app = different wallets
};

// Same user, different apps = different wallets
const ecommerceWallet = await sugLink.login("google", authCode, "solana", ecommerceConfig);
const gamingWallet = await sugLink.login("google", authCode, "solana", gamingConfig);

console.log("E-commerce wallet:", ecommerceWallet.solana?.publicKey);
console.log("Gaming wallet:", gamingWallet.solana?.publicKey);
// ✅ These will be different addresses

// Same app, same user = same wallet (deterministic)
const ecommerceWallet2 = await sugLink.login("google", authCode, "solana", ecommerceConfig);
console.log("Same app again:", ecommerceWallet2.solana?.publicKey);
// ✅ This will match ecommerceWallet.solana?.publicKey

Multi-Chain App Isolation

// DeFi app gets unique wallets across both chains
const deFiConfig = {
  clientId: "defi-client-id",
  clientSecret: "defi-client-secret",
  redirectUri: "http://localhost:3000/callback",
  appId: "defi-protocol-v2"
};

// NFT marketplace gets different wallets for same user
const nftConfig = {
  clientId: "nft-client-id", 
  clientSecret: "nft-client-secret",
  redirectUri: "http://localhost:3000/callback",
  appId: "nft-marketplace-v1"
};

const deFiWallets = await sugLink.login("google", authCode, "all", deFiConfig);
const nftWallets = await sugLink.login("google", authCode, "all", nftConfig);

// Different apps = different wallets on both chains
console.log("DeFi Solana:", deFiWallets.solana?.publicKey);
console.log("DeFi Sui:", deFiWallets.sui?.address);
console.log("NFT Solana:", nftWallets.solana?.publicKey);  // Different from DeFi
console.log("NFT Sui:", nftWallets.sui?.address);          // Different from DeFi

AppId Best Practices

// ✅ Good appId examples
const configs = {
  production: { appId: "myapp-prod-v1" },
  staging: { appId: "myapp-staging-v1" },
  development: { appId: "myapp-dev-v1" },
  
  // Different products
  ecommerce: { appId: "company-ecommerce-v1" },
  gaming: { appId: "company-gaming-v1" },
  
  // Versioning for migrations
  legacy: { appId: "myapp-v1" },
  current: { appId: "myapp-v2" }
};

// ❌ Avoid these patterns
const badConfigs = {
  noAppId: {}, // Same wallet across all apps
  dynamic: { appId: `user-${userId}` }, // Different per user
  timestamp: { appId: Date.now().toString() } // Changes every time
};

Backward Compatibility

// Existing code without appId continues to work
const legacyConfig = {
  clientId: "client-id",
  clientSecret: "client-secret",
  redirectUri: "http://localhost:3000/callback"
  // No appId = uses legacy behavior
};

const wallet = await sugLink.login("google", authCode, "solana", legacyConfig);
// ✅ Works exactly as before

TypeScript Application Example

import { SugLink } from "@dulcetlabs/suglink";

const sugLink = new SugLink();

// Step 1: Redirect user to OAuth provider
async function initiateLogin(platform: "google") {
  const state = await sugLink.generateState();
  // Store state for later verification
  sessionStorage.setItem("oauth_state", state);

  const authUrl = sugLink.getAuthUrl(platform, state);
  window.location.href = authUrl;
}

// Step 2: Handle OAuth callback
async function handleOAuthCallback() {
  const urlParams = new URLSearchParams(window.location.search);
  const code = urlParams.get("code");
  const receivedState = urlParams.get("state");
  const expectedState = sessionStorage.getItem("oauth_state");

  if (!code || !receivedState || !expectedState) {
    throw new Error("Missing OAuth parameters");
  }

  try {
    await sugLink.handleCallback("google", code, receivedState, expectedState);
    const publicKey = sugLink.getPublicKey();
    console.log("Public Key:", publicKey);

    // Clean up
    sessionStorage.removeItem("oauth_state");
    return publicKey;
  } catch (error) {
    console.error("Error during login:", error);
    throw error;
  }
}

function logout() {
  sugLink.logout();
  console.log("Logged out successfully");
}

Plain HTML/JavaScript Example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>SugLink Example</title>
    <script type="module">
      import { SugLink } from "./node_modules/@dulcetlabs/suglink/dist/esm/index.js";

      const sugLink = new SugLink();

      async function handleLogin(platform, code) {
        try {
          await sugLink.login(platform, code);
          const publicKey = sugLink.getPublicKey();
          document.getElementById(
            "publicKey"
          ).innerText = `Public Key: ${publicKey}`;
        } catch (error) {
          console.error("Login failed:", error);
        }
      }

      window.handleLogin = handleLogin;
    </script>
  </head>
  <body>
    <button onclick="handleLogin('google', 'auth_code')">
      Login with Google
    </button>
    <button onclick="handleLogin('tiktok', 'auth_code')">
      Login with TikTok
    </button>
    <button onclick="handleLogin('snapchat', 'auth_code')">
      Login with Snapchat
    </button>
    <p id="publicKey"></p>
  </body>
</html>

Direct OAuth Configuration

You can also set the OAuth configuration directly in your code:

import { SugLink } from "@dulcetlabs/suglink";

const sugLink = new SugLink();

const customConfig = {
  clientId: "your_custom_client_id",
  clientSecret: "your_custom_client_secret",
  redirectUri: "your_custom_redirect_uri",
};

async function handleLogin(
  platform: "google" | "tiktok" | "snapchat",
  code: string
) {
  try {
    const keypair = await sugLink.login(platform, code, customConfig);
    console.log("Keypair generated:", keypair.publicKey.toString());
  } catch (error) {
    console.error("Login failed:", error);
  }
}

Login Modes

SugLink supports 4 different login modes to accommodate different use cases and ensure backward compatibility:

1. 🔄 Legacy Mode (Backward Compatible)

// Existing code continues to work unchanged
const keypair = await sugLink.login("google", code);
const keypair = await sugLink.login("google", code, config);
  • Returns: Keypair (Solana only)
  • Use Case: Existing applications that only need Solana
  • Migration: No code changes required

2. 🟡 Solana Only Mode (New API)

// Explicitly request Solana using new structured API
const result = await sugLink.login("google", code, "solana");
console.log(result.solana?.publicKey); // Solana wallet details
console.log(result.sui); // undefined
  • Returns: { solana: WalletInfo, sui?: undefined }
  • Use Case: Solana-only apps using the new structured response
  • Migration: Update to use structured response format

3. 🔵 Sui Only Mode

// Generate only Sui wallet
const result = await sugLink.login("google", code, "sui");
console.log(result.sui?.address); // Sui wallet details
console.log(result.solana); // undefined
  • Returns: { solana?: undefined, sui: WalletInfo }
  • Use Case: Sui-only applications
  • Migration: New applications targeting Sui blockchain

4. 🌈 Multi-Chain Mode (Both Chains)

// Generate both Solana and Sui wallets from same social login
const result = await sugLink.login("google", code, "all");
console.log(result.solana?.publicKey); // Solana wallet
console.log(result.sui?.address); // Sui wallet
  • Returns: { solana: WalletInfo, sui: WalletInfo }
  • Use Case: Multi-chain applications supporting both blockchains
  • Migration: Full multi-chain capability

🔧 Mode Detection

SugLink automatically detects which mode to use based on parameters:

// Legacy mode: 2 or 3 parameters, third is config object
await sugLink.login("google", code);
await sugLink.login("google", code, { clientId: "..." });

// Multi-chain mode: 3+ parameters, third is chain selector string
await sugLink.login("google", code, "solana");
await sugLink.login("google", code, "sui", config);
await sugLink.login("google", code, "all", config);

API Reference

SugLink Class

Constructor

new SugLink();

Methods

  • getAuthUrl(platform, state?, config?): Generates OAuth authorization URL for the specified platform.

    • platform: 'google' (TikTok and Snapchat coming soon)
    • state: Optional state parameter for CSRF protection (recommended)
    • config: Optional OAuth configuration object
    • Returns: string (authorization URL)
  • generateState(): Generates a secure random state parameter for CSRF protection.

    • Returns: Promise (32-character hex string)
  • login(platform, code, config?): [Legacy] Logs in using social platform credentials and generates a Solana keypair.

    • platform: 'google' (TikTok and Snapchat coming soon)
    • code: OAuth authorization code
    • config: Optional OAuth configuration object
    • Returns: Promise
  • login(platform, code, chain, config?): [Multi-Chain] Logs in and generates keypair(s) for specified blockchain(s).

    • platform: 'google' (TikTok and Snapchat coming soon)
    • code: OAuth authorization code
    • chain: 'solana' | 'sui' | 'all' - which blockchain(s) to generate wallets for
    • config: Optional OAuth configuration object
    • Returns: Promise where MultiChainResult contains:
      • solana?: { keypair: Keypair, publicKey: string, privateKey: Uint8Array }
      • sui?: { keypair: SuiKeypairLike, address: string, privateKey: string }
  • handleCallback(platform, code, receivedState?, expectedState?, config?): Handles OAuth callback with state validation.

    • platform: 'google' (TikTok and Snapchat coming soon)
    • code: OAuth authorization code from callback
    • receivedState: State parameter from OAuth callback
    • expectedState: Expected state value for CSRF protection
    • config: Optional OAuth configuration object
    • Returns: Promise (legacy Solana-only)

Solana Methods

  • getKeypair(): Returns the current Solana keypair.

    • Returns: Keypair
    • Throws: Error if no keypair available
  • getPublicKey(): Returns the Solana public key as a base-58 string.

    • Returns: string
    • Throws: Error if no keypair available
  • getPrivateKey(): Returns the Solana private key as a Uint8Array.

    • Returns: Uint8Array
    • Throws: Error if no keypair available

Sui Methods

  • getSuiKeypair(): Returns the current Sui keypair (if generated via multi-chain login).

    • Returns: SuiKeypairLike
    • Throws: Error if Sui keypair hasn't been generated
  • getSuiAddress(): Returns the Sui address as a 0x-prefixed string.

    • Returns: string (e.g., "0x4779c70ab11c82f7724c63be8868663e210a79cc3f430e5dbc4ae4793761323d")
    • Throws: Error if Sui keypair hasn't been generated
  • getSuiPrivateKey(): Returns the Sui private key in Bech32 format.

    • Returns: string (e.g., "suiprivkey1qp9757x3gu64cfyxquyx73t7h7nzcmhk7nr55zwzv0whfsa4yffcvfntxfe")
    • Throws: Error if Sui keypair hasn't been generated

General Methods

  • logout(): Clears all current keypairs, effectively logging out.
    • Returns: void

SuiUtils Class

Utility class for Sui blockchain operations, available as a standalone import:

import { SuiUtils } from "@dulcetlabs/suglink";

Static Methods

  • createKeypairFromSeed(seed): Creates a Sui Ed25519 keypair from seed.

    • seed: Buffer (32+ bytes, will use first 32 bytes)
    • Returns: Promise
  • getSuiAddress(keypair): Extracts Sui address from keypair.

    • keypair: SuiKeypairLike
    • Returns: string (0x-prefixed address)
  • getSuiPrivateKeyBytes(keypair): Extracts raw private key bytes.

    • keypair: SuiKeypairLike
    • Returns: Uint8Array (32 bytes for Ed25519)
  • exportSuiKeypair(keypair): Exports keypair in official Sui format.

    • keypair: SuiKeypairLike
    • Returns: { privateKey: string, schema: string }

Example Usage

import { SuiUtils } from "@dulcetlabs/suglink";
import { Buffer } from "buffer";

// Create keypair from seed
const seed = Buffer.from("your_32_byte_seed_here");
const keypair = await SuiUtils.createKeypairFromSeed(seed);

// Get address and private key
const address = SuiUtils.getSuiAddress(keypair);
const { privateKey } = SuiUtils.exportSuiKeypair(keypair);

console.log("Address:", address);
console.log("Private Key:", privateKey);

Security Considerations

  • Never expose your OAuth client secrets in client-side code.
  • Always use environment variables for sensitive configuration.
  • Implement proper OAuth flow with state verification.
  • Store keypairs securely and never expose private keys.
  • Use HTTPS for all OAuth redirects.
  • Implement proper session management.

License

This package is provided under the MIT License with Commons Clause.

Usage Requirements

  • Permission Required: You MUST obtain explicit written permission from Dulcet Labs before using the software in production environments or incorporating it into commercial products.

Support

For support, please open an issue in the GitHub repository or contact the maintainers.

Platform Support

Social Login Platforms

SugLink currently supports:

  • Google ✅ (Fully implemented with ID token verification)
  • TikTok 🚧 (In development - OAuth endpoints configured)
  • Snapchat 🚧 (In development - OAuth endpoints configured)

Each platform requires its own OAuth credentials and proper setup in their respective developer consoles.

Blockchain Support

SugLink supports multiple blockchains:

  • Solana ✅ (Fully implemented with Ed25519 keypairs)
  • Sui ✅ (Fully implemented with Ed25519 keypairs and proper address derivation)

Both blockchains use the same deterministic seed generation, ensuring that the same social login produces consistent wallet addresses across chains.

What's New in v0.1.0

Multi-Chain Support

  • Sui Blockchain Integration: Generate Sui wallets alongside Solana wallets from the same social login
  • Chain Selection: Choose "solana", "sui", or "all" during login
  • Backward Compatibility: Existing Solana-only code continues to work without changes
  • SuiUtils Class: Standalone utility class for Sui keypair operations

🔧 Multi-Chain API

// Generate wallets for both chains
const result = await sugLink.login("google", code, "all");
console.log("Solana:", result.solana?.publicKey);
console.log("Sui:", result.sui?.address);

// Chain-specific generation
const solanaOnly = await sugLink.login("google", code, "solana");
const suiOnly = await sugLink.login("google", code, "sui");

// Legacy support (still works)
const keypair = await sugLink.login("google", code); // Returns Solana Keypair

💎 Sui Integration Features

  • Ed25519 Keypairs: Uses official Sui SDK Ed25519 implementation
  • Proper Address Format: 0x-prefixed 64-character addresses
  • Bech32 Private Keys: suiprivkey1... format for compatibility
  • Deterministic Generation: Same social login = same wallet addresses across chains

�🌐 Universal Compatibility

  • Browser Support: Full compatibility with all modern browsers using Web Crypto API
  • Node.js Support: Optimized performance using native Node.js crypto module
  • Automatic Detection: Seamlessly switches between environments without configuration

🔧 Breaking Changes

  • generateState() is now async: Updated to use secure random generation across all environments

    // Old (v0.0.x)
    const state = sugLink.generateState();
    
    // New (v0.1.0+)
    const state = await sugLink.generateState();

🚀 Technical Improvements

  • Universal Crypto: Uses Web Crypto API in browsers, Node.js crypto in server environments
  • Buffer Polyfill: Automatic Buffer support for browser compatibility
  • Enhanced Security: Cryptographically secure random generation in all environments
  • Optional Dependencies: Sui SDK is optional - only required when using Sui features

Potential Improvements

  • Enhanced Key Generation: Add salt to the buildTimeKey generation process for additional security.
  • Network Security: Add timeout settings for axios POST requests to prevent hanging connections.
  • Type Safety and Validation: Implement TypeScript strict null checks for better type safety.
  • Keypair Verification: Add methods to verify keypair generation correctness.
  • Error Handling: Enhance error messages with more detailed information.

These improvements are planned for future releases. Contributions are welcome!