A React + TypeScript reference implementation for the PinkSurfing In-App Wallet SSO system.
This frontend demonstrates how to integrate with the PinkSurfing backend's wallet-centric authentication and multi-chain wallet management system. It showcases:
- Crypto-native SSO: Users authenticate via their in-app wallet (no MetaMask/WalletConnect)
- MYBIZ Token Roles: UI adapts based on user's token holdings (Bronze β Platinum)
- Multi-chain Support: Seamless balance and transaction management across 7+ chains
- Secure Transactions: PIN-based authorization for semi-custodial wallets
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Frontend (React + Vite) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
β β Auth Store β β Wallet Store β β API Client β β
β β (Zustand) β β (Zustand) β β (Axios) β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Pages: Login | Register | Dashboard | Wallet | Transfer β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PinkSurfing Backend API β
β /auth/* (SSO) β /wallet/* (Operations) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Node.js 18+
- npm or yarn
- Backend server running on
http://localhost:5000
# Navigate to the frontend directory
cd frontend-reference
# Install dependencies
npm install
# Start development server
npm run devThe app will be available at http://localhost:3000
Create a .env file:
VITE_API_URL=http://localhost:5000src/
βββ main.tsx # App entry point
βββ App.tsx # Router configuration
βββ index.css # Global styles + Tailwind
βββ types/
β βββ index.ts # TypeScript type definitions
βββ services/
β βββ api.ts # API client with auth interceptors
βββ store/
β βββ authStore.ts # Authentication state (Zustand)
β βββ walletStore.ts # Wallet state (Zustand)
βββ components/
β βββ Layout.tsx # App shell with navigation
βββ pages/
βββ LoginPage.tsx # Email/password login
βββ RegisterPage.tsx # Account + wallet creation
βββ DashboardPage.tsx # Overview with balances
βββ WalletPage.tsx # Token list + history
βββ TransferPage.tsx # Send crypto
βββ SettingsPage.tsx # Security settings
// User creates account with wallet type selection
const response = await authApi.register({
email: 'user@example.com',
password: 'securePassword123',
walletType: 'custodial', // or 'semi-custodial'
pin: '123456', // Required for semi-custodial
});
// Response includes user, tokens, and wallet
const { user, tokens, wallet } = response;// Standard email/password login
const response = await authApi.login('user@example.com', 'password');
// Tokens are automatically stored and used for subsequent requestsThe API client automatically handles token refresh:
// Interceptor in api.ts handles 401 responses
// Attempts refresh, then retries original request// Single chain
const balance = await walletApi.getBalance(137); // Polygon
// All chains
const balances = await walletApi.getAllBalances();const result = await walletApi.transfer({
to: '0x...',
amount: '1.5',
chainId: 137,
tokenAddress: '0x...', // Optional, omit for native token
pin: '123456', // Required for semi-custodial
});The UI dynamically shows role benefits:
| Role | MYBIZ Required | Benefits |
|---|---|---|
| Guest | 0 | Basic features |
| Bronze | 100 | Reduced fees, email support |
| Silver | 1,000 | Lower fees, priority support |
| Gold | 10,000 | Minimal fees, premium features |
| Platinum | 100,000 | Zero fees, VIP support |
// Access current role
const role = useAuthStore((state) => state.user?.role);
const benefits = ROLE_BENEFITS[role];- Ethereum (1)
- Polygon (137) - Primary
- BNB Chain (56)
- Arbitrum One (42161)
- Optimism (10)
- Base (8453)
- Avalanche (43114)
import { SUPPORTED_CHAINS } from './types';
// Get chain info
const polygon = SUPPORTED_CHAINS.find(c => c.chainId === 137);For semi-custodial wallets, transactions require PIN:
<input
type="password"
inputMode="numeric"
maxLength={6}
value={pin}
onChange={(e) => setPin(e.target.value.replace(/\D/g, ''))}
className="tracking-[0.5em] text-center"
/>// Update security preferences
await walletApi.updateSecuritySettings({
twoFactorEnabled: true,
transactionNotifications: true,
loginNotifications: true,
});All API responses are fully typed:
interface WalletBalance {
chainId: number;
chainName: string;
native: { symbol: string; balance: string; balanceFormatted: string; };
tokens: TokenBalance[];
totalUsdValue?: string;
}Using Zustand with persistence:
// Auth store persists to localStorage
export const useAuthStore = create<AuthState>()(
persist(
(set, get) => ({
user: null,
tokens: null,
// ...actions
}),
{
name: 'pinksurfing-auth',
storage: createJSONStorage(() => localStorage),
}
)
);The app is fully responsive with:
- Mobile-first approach
- Bottom navigation on mobile
- Sidebar navigation on desktop
- Touch-friendly inputs
Tailwind CSS with custom theme in tailwind.config.js:
colors: {
pink: { /* PinkSurfing brand */ },
brand: {
primary: '#ec4899',
secondary: '#8b5cf6',
accent: '#06b6d4',
}
}Configure in vite.config.ts proxy or .env:
server: {
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},See full API documentation in the backend's api/routes/authRoutes.js and api/routes/walletRoutes.js.
| Endpoint | Method | Description |
|---|---|---|
/auth/login |
POST | Email/password login |
/auth/register |
POST | Create account + wallet |
/auth/refresh |
POST | Refresh access token |
/wallet/create |
POST | Create new wallet |
/wallet/balance/:chainId |
GET | Get chain balance |
/wallet/transfer |
POST | Send transaction |
- Follow existing code style
- Use TypeScript strictly
- Add types for new features
- Test on multiple chains
Proprietary - PinkSurfing Β© 2024