Skip to content

SDK Package (@humanwallet/sdk)

HumanWallet SDK@humanwallet/sdk

The @humanwallet/sdk package provides a high-level, class-based client interface for HumanWallet. Built on top of @humanwallet/core, it offers a simplified API that makes it easy to integrate passkey-based authentication and account abstraction into your Web3 applications.

Overview

The SDK abstracts away the complexity of the core functions by providing:

  • Simple Client Interface: Easy-to-use class-based API for all wallet operations
  • Automatic State Management: Built-in session handling and account state tracking
  • Streamlined Configuration: Minimal setup required compared to core functions
  • Comprehensive Operations: Support for authentication, contract interactions, and transaction management
  • TypeScript First: Excellent developer experience with full type safety

When to Use SDK vs Core

Use SDK When:

  • You want a simple, high-level interface
  • You're building a new application from scratch
  • You prefer class-based APIs
  • You want automatic state management
  • You need quick prototyping capabilities

Use Core When:

  • You need fine-grained control over the authentication flow
  • You're integrating into existing state management systems
  • You want to implement custom session handling
  • You're building a framework or library on top of HumanWallet

Installation

npm install @humanwallet/sdk

Browser Compatibility

When using the SDK in browser environments, you need to configure Buffer:

npm install buffer

Create a polyfills file:

// src/polyfills.ts
import { Buffer } from "buffer"
 
// Make Buffer available globally
window.Buffer = Buffer

Import it at the beginning of your main entry file:

import "./polyfills"
// ... rest of your imports

And add to your index.html <head>:

<script>
  var global = global || window
</script>

For more details, see the React Integration Guide.

Quick Start

import { HumanWallet } from "@humanwallet/sdk"
import { sepolia } from "viem/chains"
 
// Initialize the client
const client = new HumanWallet(
  "your-project-id", // projectId
  sepolia, // chain
)
 
// Connect with existing passkey
const address = await client.connect()
console.log("Connected:", address)
 
// Or register a new user
// const address = await client.register("alice")
 
// Check connection status
if (client.address) {
  console.log("User address:", client.address)
 
  // Execute a contract transaction
  const hash = await client.writeContract({
    address: "0x...",
    abi: contractAbi,
    functionName: "transfer",
    args: ["0x742d35Cc6634C0532925a3b8D52ECC5c5e9fA008", BigInt("1000000000000000000")],
  })
 
  console.log("Transaction hash:", hash)
}
 
// Disconnect when done
await client.disconnect()

HumanWallet Class

The main SDK interface is the HumanWallet class that encapsulates all HumanWallet functionality.

Constructor

new HumanWallet(
  projectId: string,
  chain: Chain
)

Parameters

  • projectId: Your ZeroDev project ID for accessing bundler and paymaster services
  • chain: Viem chain configuration object (e.g., sepolia, mainnet)

Example

import { sepolia, polygon, mainnet } from "viem/chains"
 
// Sepolia testnet client
const sepoliaClient = new HumanWallet(
  "your-project-id",
  sepolia,
)
 
// Mainnet client
const mainnetClient = new HumanWallet(
  "your-project-id",
  mainnet,
)

Properties

address: Address | undefined

The current user's wallet address. Returns undefined if no user is authenticated.

if (client.address) {
  console.log("Current address:", client.address)
} else {
  console.log("No user authenticated")
}

chain: Chain

The current blockchain network the client is connected to.

console.log("Current chain:", client.chain.name) // "Sepolia"
console.log("Chain ID:", client.chain.id) // 11155111

API Reference

Authentication Methods

  • connect() - Connect with existing passkey
  • register() - Create new passkey-based accounts
  • reconnect() - Restore previous sessions
  • disconnect() - End sessions and clear data
  • hasAccount() - Check if user has existing account

Contract Interaction Methods

  • writeContract() - Execute contract transactions
  • writeContracts() - Execute batch transactions
  • readContract() - Read contract state
  • signTypedData() - Sign structured data

Transaction Management Methods

  • waitForTransaction() - Wait for transaction confirmation
  • waitForUserOperation() - Wait for user operation completion

Usage Patterns

Single Page Application (SPA)

class WalletService {
  private client: HumanWallet
 
  constructor() {
    this.client = new HumanWallet(
      process.env.VITE_PROJECT_ID!,
      sepolia,
    )
  }
 
  async initializeSession() {
    // Try to restore previous session
    const address = await this.client.reconnect()
    if (address) {
      console.log("Session restored for:", address)
      return true
    }
    return false
  }
 
  async authenticateUser(username: string, isNewUser: boolean = false) {
    if (isNewUser) {
      return await this.client.register(username)
    } else {
      return await this.client.connect()
    }
  }
 
  async transferTokens(to: string, amount: bigint) {
    if (!this.client.address) {
      throw new Error("User not authenticated")
    }
 
    return await this.client.writeContract({
      address: TOKEN_CONTRACT_ADDRESS,
      abi: ERC20_ABI,
      functionName: "transfer",
      args: [to, amount],
    })
  }
 
  async signOut() {
    await this.client.disconnect()
  }
 
  get userAddress() {
    return this.client.address
  }
 
  get isAuthenticated() {
    return !!this.client.address
  }
}

React Integration

import { createContext, useContext, useState, useEffect } from 'react'
import { HumanWallet } from '@humanwallet/sdk'
 
const WalletContext = createContext<{
  client: HumanWallet
  address?: string
  isConnected: boolean
  connect: () => Promise<string>
  register: (username: string) => Promise<string>
  disconnect: () => Promise<void>
}>()
 
export function WalletProvider({ children }: { children: React.ReactNode }) {
  const [client] = useState(() => new HumanWallet(
    process.env.VITE_PROJECT_ID!,
    sepolia
  ))
 
  const [address, setAddress] = useState<string>()
  const [isConnected, setIsConnected] = useState(false)
 
  useEffect(() => {
    // Try to restore session on mount
    client.reconnect().then(addr => {
      if (addr) {
        setAddress(addr)
        setIsConnected(true)
      }
    })
  }, [client])
 
  const connect = async () => {
    const addr = await client.connect()
    setAddress(addr)
    setIsConnected(true)
    return addr
  }
 
  const register = async (username: string) => {
    const addr = await client.register(username)
    setAddress(addr)
    setIsConnected(true)
    return addr
  }
 
  const disconnect = async () => {
    await client.disconnect()
    setAddress(undefined)
    setIsConnected(false)
  }
 
  return (
    <WalletContext.Provider value={{
      client,
      address,
      isConnected,
      connect,
      register,
      disconnect
    }}>
      {children}
    </WalletContext.Provider>
  )
}
 
export const useWallet = () => {
  const context = useContext(WalletContext)
  if (!context) {
    throw new Error('useWallet must be used within WalletProvider')
  }
  return context
}

Error Handling

The SDK methods can throw various errors that should be handled appropriately:

try {
  const address = await client.register("username")
  console.log("Registration successful:", address)
} catch (error) {
  if (error.message.includes("User rejected")) {
    console.log("User cancelled passkey creation")
  } else if (error.message.includes("not supported")) {
    console.log("Passkeys not supported in this browser")
  } else if (error.message.includes("already exists")) {
    console.log("Username already taken")
  } else {
    console.error("Registration failed:", error)
  }
}

Common Error Patterns

class ErrorHandler {
  static handleAuthError(error: Error) {
    if (error.message.includes("User rejected")) {
      return "Authentication cancelled by user"
    } else if (error.message.includes("not supported")) {
      return "Passkeys not supported in this browser"
    } else if (error.message.includes("Account not created")) {
      return "Failed to create account"
    } else {
      return "Authentication failed"
    }
  }
 
  static handleTransactionError(error: Error) {
    if (error.message.includes("User not authenticated")) {
      return "Please login to perform transactions"
    } else if (error.message.includes("insufficient funds")) {
      return "Insufficient balance for transaction"
    } else if (error.message.includes("execution reverted")) {
      return "Contract execution failed"
    } else {
      return "Transaction failed"
    }
  }
}

Best Practices

1. Environment Configuration

// config.ts
const getSDKConfig = () => {
  const requiredEnvVars = ["VITE_PROJECT_ID"]
 
  for (const envVar of requiredEnvVars) {
    if (!process.env[envVar]) {
      throw new Error(`Missing required environment variable: ${envVar}`)
    }
  }
 
  return {
    projectId: process.env.VITE_PROJECT_ID!,
  }
}
 
export const createWalletClient = (chain: Chain) => {
  const config = getSDKConfig()
  return new HumanWallet(config.projectId, chain)
}

2. Session Management

// Always try to reconnect on app startup
async function initializeApp() {
  const client = createWalletClient(sepolia)
 
  try {
    const address = await client.reconnect()
    if (address) {
      console.log("User session restored:", address)
      return { client, address }
    }
  } catch (error) {
    console.log("No previous session found")
  }
 
  return { client, address: null }
}

3. Type Safety

// Define your contract interfaces
interface TokenContract {
  address: `0x${string}`
  abi: typeof ERC20_ABI
}
 
class TokenService {
  constructor(
    private client: ClientRepository,
    private tokenContract: TokenContract,
  ) {}
 
  async getBalance(): Promise<bigint> {
    return await this.client.readContract({
      address: this.tokenContract.address,
      abi: this.tokenContract.abi,
      functionName: "balanceOf",
      args: [this.client.address!],
    })
  }
 
  async transfer(to: `0x${string}`, amount: bigint): Promise<string> {
    return await this.client.writeContract({
      address: this.tokenContract.address,
      abi: this.tokenContract.abi,
      functionName: "transfer",
      args: [to, amount],
    })
  }
}

Migration from Core

If you're currently using @humanwallet/core, migrating to the SDK is straightforward:

Before (Core)

import { createConfig, register, login, writeContract } from "@humanwallet/core"
 
const config = createConfig({
  passkeyUrl: "https://passkeys.example.com/api/v3/your-project-id",
  bundlerRpc: "https://rpc.example.com/api/v3/your-project-id/chain/11155111",
  paymasterRpc: "https://rpc.example.com/api/v3/your-project-id/chain/11155111",
  chain: sepolia,
})
const { sessionKeyAccount, kernelClient } = await register("username", config)
 
// Update config with authenticated clients
const authenticatedConfig = { ...config, sessionKeyAccount, kernelClient }
const hash = await writeContract(authenticatedConfig, contractParams)

After (SDK)

import { HumanWallet } from "@humanwallet/sdk"
 
const client = new HumanWallet("your-project-id", sepolia)
await client.register("username")
 
const hash = await client.writeContract(contractParams)

Next Steps