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
Name | Type | Required | Description |
---|---|---|---|
apiKey | string | Yes | Your SBC API key |
chain | Chain | Yes | Blockchain network (e.g., baseSepolia ) |
wallet | string | No | Wallet type: 'auto' , 'metamask' , 'coinbase' , 'walletconnect' |
walletOptions | WalletOptions | No | Wallet connection preferences |
rpcUrl | string | No | Custom RPC URL |
debug | boolean | No | Enable debug logging |
logging | LoggingConfig | No | Production logging configuration |
WalletOptions
Name | Type | Description |
---|---|---|
projectId | string | WalletConnect project ID |
autoConnect | boolean | Automatically connect on init |
preferredWallets | string[] | Order of wallet preference |
customOptions | object | Custom 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
Aspect | Next.js Backend | React Wallet |
---|---|---|
Security | Private key on server (most secure) | Private key in user wallet |
User Experience | No wallet connection needed | User connects their wallet |
Use Case | Backend services, bots, automation | User-facing dApps |
Deployment | Smart account deployed once | Smart account per user |
Gas Fees | Covered by SBC paymaster | Covered 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:
- React Basic - Simple demo for learning the basics
- Backend - Node.js backend integration
- Next.js Backend - Next.js with server-side private key management
- React Wallet - User wallet integration with MetaMask
Next Steps
- Explore supported Smart Account Implementations to understand the underlying wallet technology.
- Understand the Architecture of SBC's account abstraction solutions.
- Check out the API Reference for detailed documentation.
- Reach out on Telegram for support or to request testnet SBC tokens for development.