Skip to content

SBC AppKit

Overview

The SBC AppKit is a comprehensive SDK designed to simplify the integration of account abstraction features into your applications. It provides both a core TypeScript SDK and React components for seamless integration.

With the AppKit, developers can easily enable gasless user operations, leveraging SBC's custom paymaster and bundler infrastructure without needing to manage the underlying complexities of ERC-4337 standards.

Key Features

  • Gasless Transactions: Allow users to interact with your application without needing ETH for gas fees, as SBC's paymaster sponsors transactions.
  • Simplified API: Use a straightforward API key to connect to SBC's bundler and paymaster services, abstracting the intricate details of UserOperation handling.
  • Smart Account Integration: Uses Kernel Smart Account from ZeroDev by default, enabling advanced wallet functionalities.
  • React Integration: Complete React hooks and components for seamless frontend integration.
  • Wallet Management: Automatic wallet detection and connection for MetaMask, Coinbase Wallet, WalletConnect, and more.
  • Production Logging: Structured logging system for production environments.
  • Developer-Friendly: Comprehensive guides and examples to get started quickly.

Getting Started

To integrate the SBC AppKit into your project, follow these steps:

1. Obtain an API Key

Visit the SBC Dashboard. Sign in with your wallet and click on Create Key. If you need any assistance, reach out to us on Telegram.

2. Install the SDK

The AppKit is available on npm with separate packages for core functionality and React integration:

# For React applications (recommended)
npm install @stablecoin.xyz/react @stablecoin.xyz/core viem
 
# For backend/Node.js applications (core only)
npm install @stablecoin.xyz/core viem

3. Choose Your Integration Approach

SBC AppKit supports multiple approaches for wallet management:

A. React x Wallet Integration (Recommended for Frontend)

Perfect for React applications with automatic wallet detection and connection.

import { SbcProvider, useSbcApp, WalletButton } from '@stablecoin.xyz/react';
import { baseSepolia } from 'viem/chains';
 
// 1. Configure the provider
const config = {
  apiKey: process.env.REACT_APP_SBC_API_KEY!,
  chain: baseSepolia,
  wallet: 'auto', // Automatically detect available wallets
  debug: true
};
 
// 2. Wrap your app
function App() {
  return (
    <SbcProvider config={config}>
      <YourApp />
    </SbcProvider>
  );
}
 
// 3. Use the hooks and components in your app
function TransactionComponent() {
  const { account, ownerAddress, isLoadingAccount } = useSbcApp();
 
  return (
    <div>
      <WalletButton>Connect Wallet</WalletButton>
      {account && (
        <div>
          <h2>Smart Account: {account.address}</h2>
          <p>Owner: {ownerAddress}</p>
          <p>Balance: {account.balance} wei</p>
          <p>Deployed: {account.isDeployed ? 'Yes' : 'No'}</p>
        </div>
      )}
    </div>
  );
}

B. Core SDK Integration (Backend/Advanced)

For backend applications or advanced use cases with full control over wallet management.

import { SbcAppKit } from '@stablecoin.xyz/core';
import { baseSepolia } from 'viem/chains';
 
// Initialize with private key (backend)
const appKit = new SbcAppKit({
  apiKey: 'your-sbc-api-key',
  chain: baseSepolia,
  privateKey: '0x1234567890abcdef...' // Your private key
});
 
// OR initialize with wallet client (frontend)
const appKit = new SbcAppKit({
  apiKey: 'your-sbc-api-key',
  chain: baseSepolia,
  walletClient: yourWalletClient // Pre-configured wallet client
});
 
// Get account info
const account = await appKit.getAccount();
console.log('Smart Account:', account.address);
console.log('Deployed:', account.isDeployed);
console.log('Nonce:', account.nonce);
console.log('Balance:', account.balance);

4. Send Gasless Transactions

When initialized, a smart account is created behind the scenes, owned by your wallet.

You can send gasless transactions using the same API regardless of your chosen integration approach:

// Single transaction example 
const result = await appKit.sendUserOperation({
  to: '0x742d35Cc6641C4532B4d4c7B4C0D1C3d4e5f6789',
  data: '0x',
  value: '1000000000000000000' // 1 ETH in wei
});
 
console.log('Transaction Hash:', result.transactionHash);
 
// Batch transactions example
const batchResult = await appKit.sendUserOperation({
  calls: [
    {
      to: '0x742d35Cc6641C4532B4d4c7B4C0D1C3d4e5f6789',
      data: '0x',
      value: 500000000000000000n // 0.5 ETH
    },
    {
      to: '0x1234567890123456789012345678901234567890',
      data: '0xa9059cbb...', // Encoded contract call
      value: 0n
    }
  ]
});

5. React Hooks for Transaction Management

For React applications, use the useUserOperation hook for transaction management:

import { useUserOperation } from '@stablecoin.xyz/react';
 
function TransactionComponent() {
  const { sendUserOperation, isLoading, isSuccess, error } = useUserOperation({
    onSuccess: (result) => console.log('Success:', result),
    onError: (error) => console.error('Error:', error)
  });
 
  const handleSend = async () => {
    await sendUserOperation({
      to: '0x...',
      data: '0x',
      value: '1000000000000000000'
    });
  };
 
  return (
    <div>
      <button onClick={handleSend} disabled={isLoading}>
        {isLoading ? 'Sending...' : 'Send 1 ETH'}
      </button>
      {isSuccess && <p>Transaction sent!</p>}
      {error && <p>Error: {error.message}</p>}
    </div>
  );
}

6. Optional: Production Logging

For production environments, configure structured logging:

import { SbcAppKit, createHttpLogger } from '@stablecoin.xyz/core';
 
const appKit = new SbcAppKit({
  apiKey: 'your-sbc-api-key',
  chain: baseSepolia,
  debug: true, // Enable console logging for development
  logging: {
    enabled: true,
    level: 'info',
    logger: createHttpLogger('https://your-logging-endpoint.com/logs', {
      'Authorization': 'Bearer your-token'
    }),
    context: {
      appName: 'My dApp',
      environment: 'production',
      version: '1.0.0'
    }
  }
});

React Components

SbcProvider

The main provider component that wraps your app and provides SBC context.

<SbcProvider config={config}>
  {/* your app */}
</SbcProvider>

WalletButton

A button component for connecting to wallets with built-in loading and error states.

<WalletButton 
  walletType="auto"
  onConnect={(result) => console.log('Connected:', result)}
  onError={(error) => console.error('Error:', error)}
>
  Connect Wallet
</WalletButton>

WalletSelector

A component for displaying and selecting from available wallets.

<WalletSelector
  onConnect={(result) => console.log('Connected:', result)}
  showOnlyAvailable={true}
/>

React Hooks

useSbcApp

Main hook for accessing SBC state and actions.

const {
  sbcAppKit,        // SBC AppKit instance
  isInitialized,    // SDK initialized
  error,            // Initialization error
  account,          // Smart account info
  isLoadingAccount, // Loading state
  accountError,     // Error loading account
  refreshAccount,   // Refresh account info
  ownerAddress,     // EOA (wallet) address
  disconnectWallet, // Disconnect wallet
} = useSbcApp();

useUserOperation

Hook for managing user operations with loading states and callbacks.

const {
  sendUserOperation, // Function to send operations
  isLoading,         // Loading state
  isSuccess,         // Success state
  error,             // Error state
} = useUserOperation({
  onSuccess: (result) => console.log('Success:', result),
  onError: (error) => console.error('Error:', error)
});

Configuration Options

SbcProvider Config

NameTypeRequiredDescription
apiKeystringYesYour SBC API key
chainChainYesBlockchain network (e.g., baseSepolia)
walletstringNoWallet type: 'auto', 'metamask', 'coinbase', 'walletconnect'
walletOptionsWalletOptionsNoWallet connection preferences
rpcUrlstringNoCustom RPC URL
debugbooleanNoEnable debug logging
loggingLoggingConfigNoProduction logging configuration

WalletOptions

NameTypeDescription
projectIdstringWalletConnect project ID
autoConnectbooleanAutomatically connect on init
preferredWalletsstring[]Order of wallet preference
customOptionsobjectCustom wallet connection options

Examples

The AppKit includes comprehensive examples in the examples/ directory. Here are detailed examples for the most common use cases:

Next.js Backend Example (Secure Production Pattern)

This example shows how to use SBC AppKit in a Next.js application with server-side private key management for maximum security.

📖 Full Example: View complete source code on GitHub

1. Environment Setup

Create a .env.local file:

# .env.local
SBC_API_KEY=your_sbc_api_key_here
RPC_URL=your_rpc_url
OWNER_PRIVATE_KEY=0x1234567890abcdef...  # Your private key
 
NEXT_PUBLIC_SBC_API_KEY=your_sbc_api_key_here  # For client-side config
NEXT_PUBLIC_CHAIN="baseSepolia" # "base" or "baseSepolia"
NEXT_PUBLIC_RPC_URL=your_rpc_url

2. Server-Side SBC Setup

// app/lib/sbc-server.ts
import { SbcAppKit } from '@stablecoin.xyz/core';
import { baseSepolia } from 'viem/chains';
import { type Hex } from 'viem';
 
const apiKey = process.env.SBC_API_KEY;
const privateKey = process.env.OWNER_PRIVATE_KEY as Hex;
 
const config = {
  chain: baseSepolia,
  apiKey,
  privateKey,
  rpcUrl,
};
 
// Singleton instance for server-side operations
let sbcAppKitInstance: SbcAppKit | null = null;
 
export async function getSbcAppKit(): Promise<SbcAppKit> {
  if (!sbcAppKitInstance) {
    sbcAppKitInstance = new SbcAppKit(config);
    
    // Deploy smart account if needed
    try {
      const accountInfo = await sbcAppKitInstance.getAccount();
      if (!accountInfo.isDeployed) {
        await sbcAppKitInstance.sendUserOperation({
          to: '0x0000000000000000000000000000000000000000',
          data: '0x',
          value: '0',
        });
      }
    } catch (err) {
      console.error('Failed to deploy smart account:', err);
    }
  }
  return sbcAppKitInstance;
}

3. API Routes

// app/api/account-info/route.ts
import { NextResponse } from 'next/server';
import { getSbcAppKit } from '@/app/lib/sbc-server';
 
export async function GET() {
  const kit = await getSbcAppKit();
  const account = await kit.getAccount();
  const ownerAddress = kit.getOwnerAddress();
 
  return NextResponse.json({
    ...account,
    ownerAddress,
  });
}
// app/api/send-transaction/route.ts
import { NextResponse } from 'next/server';
import { getSbcAppKit } from '@/app/lib/sbc-server';
 
export async function POST(request: Request) {
  try {
    const { to, value, data } = await request.json();
    const kit = await getSbcAppKit();
    
    const result = await kit.sendUserOperation({
      to,
      value: value || '0',
      data: data || '0x',
    });
 
    return NextResponse.json(result);
  } catch (error) {
    return NextResponse.json(
      { error: error instanceof Error ? error.message : 'Unknown error' },
      { status: 500 }
    );
  }
}

4. Client-Side Component

// app/page.tsx
'use client';
 
import { SbcProvider, useSbcApp } from '@stablecoin.xyz/react';
import { baseSepolia } from 'viem/chains';
 
// (optional) custom RPC URL
const rpcUrl = process.env.NEXT_PUBLIC_RPC_URL;
 
// Client-side config (no private key)
const config = {
  apiKey: process.env.NEXT_PUBLIC_SBC_API_KEY!,
  chain: baseSepolia,
  debug: true,
  rpcUrl,
};
 
function TransactionComponent() {
  const { account, isLoadingAccount } = useSbcApp();
  const [isLoading, setIsLoading] = useState(false);
 
  const handleSendTransaction = async () => {
    setIsLoading(true);
    try {
      const response = await fetch('/api/send-transaction', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          to: '0x742d35Cc6641C4532B4d4c7B4C0D1C3d4e5f6789',
          value: '1000000000000000000', // 1 ETH
        }),
      });
      
      const result = await response.json();
      console.log('Transaction sent:', result);
    } catch (error) {
      console.error('Transaction failed:', error);
    } finally {
      setIsLoading(false);
    }
  };
 
  return (
    <div>
      <h2>Smart Account: {account?.address}</h2>
      <button onClick={handleSendTransaction} disabled={isLoading}>
        {isLoading ? 'Sending...' : 'Send Transaction'}
      </button>
    </div>
  );
}
 
export default function App() {
  return (
    <SbcProvider config={config}>
      <TransactionComponent />
    </SbcProvider>
  );
}

React Wallet Example (User Wallet Integration)

This example shows how to integrate with user wallets (MetaMask, Coinbase Wallet, etc.) for true decentralized smart accounts.

📖 Full Example: View complete source code on GitHub

1. Environment Setup

Create a .env file:

# .env
VITE_SBC_API_KEY=your_sbc_api_key_here
VITE_CHAIN="baseSepolia" # "base" or "baseSepolia"
VITE_RPC_URL=your_rpc_url

2. Main App Component

// src/App.tsx
import { SbcProvider, WalletButton, useSbcApp, useUserOperation } from '@stablecoin.xyz/react';
import { baseSepolia } from 'viem/chains';
 
const config = {
  apiKey: import.meta.env.VITE_SBC_API_KEY,
  chain: baseSepolia,
  rpcUrl,
  wallet: 'auto', // Automatically detect available wallets
  debug: true,
  walletOptions: { autoConnect: false },
};
 
function WalletStatus() {
  const { ownerAddress, disconnectWallet } = useSbcApp();
  const [balances, setBalances] = useState({ eth: '0', sbc: '0' });
 
  useEffect(() => {
    if (!ownerAddress) return;
    
    // Fetch wallet balances
    const fetchBalances = async () => {
      // Implementation to fetch ETH and SBC balances
    };
    
    fetchBalances();
  }, [ownerAddress]);
 
  if (!ownerAddress) return null;
 
  return (
    <div className="wallet-status">
      <h3>✅ Wallet Connected</h3>
      <p>EOA: {ownerAddress}</p>
      <p>ETH: {balances.eth}</p>
      <p>SBC: {balances.sbc}</p>
      <button onClick={disconnectWallet}>Disconnect</button>
    </div>
  );
}
 
function SmartAccountInfo() {
  const { account, refreshAccount } = useSbcApp();
 
  return (
    <div className="smart-account-info">
      <h3>Smart Account</h3>
      <p>Address: {account?.address}</p>
      <p>Deployed: {account?.isDeployed ? 'Yes' : 'No'}</p>
      <p>Balance: {account?.balance} wei</p>
      <button onClick={refreshAccount}>Refresh</button>
    </div>
  );
}
 
function TransactionForm() {
  const { account } = useSbcApp();
  const [amount, setAmount] = useState('1');
  const [recipient, setRecipient] = useState('');
 
  const { sendUserOperation, isLoading, isSuccess, data, error } = useUserOperation({
    onSuccess: (result) => console.log('Transaction successful:', result),
    onError: (error) => console.error('Transaction failed:', error),
  });
 
  const handleSendTransaction = async () => {
    if (!account || !recipient || !amount) return;
 
    await sendUserOperation({
      to: recipient,
      data: '0x', // For ETH transfer
      value: parseUnits(amount, 18).toString(),
    });
  };
 
  return (
    <div className="transaction-form">
      <h3>Send Transaction</h3>
      <input
        type="text"
        placeholder="Recipient Address"
        value={recipient}
        onChange={(e) => setRecipient(e.target.value)}
      />
      <input
        type="number"
        placeholder="Amount (ETH)"
        value={amount}
        onChange={(e) => setAmount(e.target.value)}
      />
      <button 
        onClick={handleSendTransaction} 
        disabled={isLoading || !account}
      >
        {isLoading ? 'Sending...' : 'Send Transaction'}
      </button>
      
      {isSuccess && (
        <div className="success">
Transaction sent! Hash: {data?.transactionHash}
        </div>
      )}
      
      {error && (
        <div className="error">
Error: {error.message}
        </div>
      )}
    </div>
  );
}
 
function WalletConnectFlow() {
  const { ownerAddress } = useSbcApp();
 
  if (!ownerAddress) {
    return (
      <div className="connect-wallet">
        <h3>🔗 Connect Your Wallet</h3>
        <p>Connect your wallet to create a smart account</p>
        <WalletButton>Connect Wallet</WalletButton>
      </div>
    );
  }
 
  return (
    <>
      <WalletStatus />
      <SmartAccountInfo />
      <TransactionForm />
    </>
  );
}
 
export default function App() {
  return (
    <SbcProvider config={config}>
      <div className="app">
        <h1>SBC Wallet Integration</h1>
        <WalletConnectFlow />
      </div>
    </SbcProvider>
  );
}

Key Differences Between Examples

AspectNext.js BackendReact Wallet
SecurityPrivate key on server (most secure)Private key in user wallet
User ExperienceNo wallet connection neededUser connects their wallet
Use CaseBackend services, bots, automationUser-facing dApps
DeploymentSmart account deployed onceSmart account per user
Gas FeesCovered by SBC paymasterCovered by SBC paymaster

Both examples demonstrate the same core functionality but with different security models suitable for different use cases.

Additional Examples

For more examples and advanced usage patterns, check out the complete examples directory on GitHub:

Next Steps