https://github.com/razedotbot/solana-ui
Solana Multi Wallet Trading Platform - Pump.fun • Raydium • Launchpad • Letsbonk
https://github.com/razedotbot/solana-ui
blockchain bonk bonkbot bundler devtools jupiter launchlab launchpad letsbonk memecoin moonshot pumpfun pumpswap raydium solana solana-volume-bot solanabot solanabundler
Last synced: 5 months ago
JSON representation
Solana Multi Wallet Trading Platform - Pump.fun • Raydium • Launchpad • Letsbonk
- Host: GitHub
- URL: https://github.com/razedotbot/solana-ui
- Owner: razedotbot
- License: mit
- Created: 2025-04-14T02:02:29.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-07-24T21:58:55.000Z (6 months ago)
- Last Synced: 2025-07-24T22:07:04.980Z (6 months ago)
- Topics: blockchain, bonk, bonkbot, bundler, devtools, jupiter, launchlab, launchpad, letsbonk, memecoin, moonshot, pumpfun, pumpswap, raydium, solana, solana-volume-bot, solanabot, solanabundler
- Language: TypeScript
- Homepage: https://sol.app.raze.bot/
- Size: 1.18 MB
- Stars: 29
- Watchers: 0
- Forks: 20
- Open Issues: 0
-
Metadata Files:
- Readme: README-IFRAME-INTEGRATION.md
- License: LICENSE
- Audit: AUDIT.md
- Security: SECURITY.md
Awesome Lists containing this project
README
# Trading App Iframe Integration with TypeScript
This guide shows how to integrate the trading app as an iframe in your TypeScript application and control wallet whitelisting programmatically.
## Table of Contents
- [Installation](#installation)
- [Basic Setup](#basic-setup)
- [TypeScript Types](#typescript-types)
- [API Reference](#api-reference)
- [Usage Examples](#usage-examples)
- [Security Considerations](#security-considerations)
- [Error Handling](#error-handling)
## Installation
1. Embed the trading app as an iframe in your application:
```html
```
## Basic Setup
```typescript
// Get iframe reference
const iframe = document.getElementById('trading-iframe') as HTMLIFrameElement;
// Listen for messages from iframe
window.addEventListener('message', (event: MessageEvent) => {
if (event.source !== iframe.contentWindow) return;
handleIframeMessage(event.data);
});
// Send message to iframe
function sendMessage(message: IframeMessage): void {
iframe.contentWindow?.postMessage(message, '*');
}
```
## TypeScript Types
```typescript
// Wallet types
interface Wallet {
address: string;
label?: string;
}
interface WhitelistItem {
id: string;
address: string;
label?: string;
isActive: boolean;
addedAt: number;
}
// Message types sent TO iframe
type IframeMessage =
| AddWalletsMessage
| ClearWalletsMessage
| GetWalletsMessage;
interface AddWalletsMessage {
type: 'ADD_WALLETS';
wallets: (string | Wallet)[];
}
interface ClearWalletsMessage {
type: 'CLEAR_WALLETS';
}
interface GetWalletsMessage {
type: 'GET_WALLETS';
}
// Response types received FROM iframe
type IframeResponse =
| IframeReadyResponse
| WalletsAddedResponse
| WalletsClearedResponse
| CurrentWalletsResponse;
interface IframeReadyResponse {
type: 'IFRAME_READY';
}
interface WalletsAddedResponse {
type: 'WALLETS_ADDED';
success: boolean;
count: number;
}
interface WalletsClearedResponse {
type: 'WALLETS_CLEARED';
success: boolean;
}
interface CurrentWalletsResponse {
type: 'CURRENT_WALLETS';
wallets: WhitelistItem[];
}
```
## API Reference
### Commands (Sent to Iframe)
#### ADD_WALLETS
Adds wallets to the whitelist.
```typescript
interface AddWalletsMessage {
type: 'ADD_WALLETS';
wallets: (string | Wallet)[];
}
```
**Wallet Formats:**
- String: `"ABCD"` or `"ABCD:Label"`
- Object: `{ address: "ABCD", label: "Label" }`
#### CLEAR_WALLETS
Removes all iframe-added wallets from the whitelist.
```typescript
interface ClearWalletsMessage {
type: 'CLEAR_WALLETS';
}
```
#### GET_WALLETS
Retrieves current whitelist.
```typescript
interface GetWalletsMessage {
type: 'GET_WALLETS';
}
```
### Responses (Received from Iframe)
#### IFRAME_READY
Sent when iframe is loaded and ready for communication.
#### WALLETS_ADDED
Confirms wallets were added successfully.
#### WALLETS_CLEARED
Confirms iframe wallets were cleared.
#### CURRENT_WALLETS
Returns current whitelist data.
#### WHITELIST_TRADING_STATS
Sent automatically whenever whitelist trading statistics update. Contains real-time trading data for whitelisted addresses.
```typescript
interface WhitelistTradingStatsResponse {
type: 'WHITELIST_TRADING_STATS';
data: {
bought: number; // Total SOL bought by whitelisted addresses
sold: number; // Total SOL sold by whitelisted addresses
net: number; // Net SOL (sold - bought)
trades: number; // Total number of trades
solPrice: number; // Current SOL price in USD
timestamp: number; // When the stats were calculated
};
}
```
#### SOL_PRICE_UPDATE
Sent automatically whenever the SOL price updates. Provides real-time SOL price data.
```typescript
interface SolPriceUpdateResponse {
type: 'SOL_PRICE_UPDATE';
data: {
solPrice: number; // Current SOL price in USD
timestamp: number; // When the price was updated
};
}
```
#### WHITELIST_TRADE
Sent automatically for each individual trade made by whitelisted addresses. Provides real-time individual trade data.
```typescript
interface WhitelistTradeResponse {
type: 'WHITELIST_TRADE';
data: {
type: 'buy' | 'sell'; // Trade type
address: string; // Trader's wallet address (signer)
tokensAmount: number; // Amount of tokens traded
avgPrice: number; // Average price per token
solAmount: number; // SOL amount involved in trade
timestamp: number; // When the trade occurred
signature: string; // Transaction signature
};
}
```
#### TOKEN_PRICE_UPDATE
Sent automatically for every trade (including non-whitelisted). Provides real-time token price updates from all trading activity.
```typescript
interface TokenPriceUpdateResponse {
type: 'TOKEN_PRICE_UPDATE';
data: {
tokenPrice: number; // Current token price from latest trade
tokenMint: string; // Token mint address
timestamp: number; // When the trade occurred
tradeType: 'buy' | 'sell'; // Type of trade that updated the price
volume: number; // SOL volume of the trade
};
}
```
## Usage Examples
### 1. Complete TypeScript Class
```typescript
class TradingAppIframe {
private iframe: HTMLIFrameElement;
private isReady: boolean = false;
private messageQueue: IframeMessage[] = [];
constructor(iframeId: string) {
this.iframe = document.getElementById(iframeId) as HTMLIFrameElement;
this.setupMessageListener();
}
private setupMessageListener(): void {
window.addEventListener('message', (event: MessageEvent) => {
if (event.source !== this.iframe.contentWindow) return;
this.handleMessage(event.data);
});
}
private handleMessage(data: IframeResponse): void {
switch (data.type) {
case 'IFRAME_READY':
this.isReady = true;
this.processMessageQueue();
break;
case 'WALLETS_ADDED':
console.log(`Successfully added ${data.count} wallets`);
break;
case 'WALLETS_CLEARED':
console.log('Cleared all iframe wallets');
break;
case 'CURRENT_WALLETS':
console.log('Current wallets:', data.wallets);
break;
case 'WHITELIST_TRADING_STATS':
console.log('Trading stats updated:', {
bought: `${data.data.bought.toFixed(3)} SOL`,
sold: `${data.data.sold.toFixed(3)} SOL`,
net: `${data.data.net.toFixed(3)} SOL`,
trades: data.data.trades,
solPrice: `$${data.data.solPrice.toFixed(2)}`
});
// You can update your UI here with the new trading statistics
this.updateTradingStatsUI(data.data);
break;
case 'SOL_PRICE_UPDATE':
console.log('SOL price updated:', `$${data.data.solPrice.toFixed(2)}`);
// You can update your UI here with the new SOL price
this.updateSolPriceUI(data.data.solPrice);
break;
case 'WHITELIST_TRADE':
console.log('New whitelist trade:', {
type: data.data.type,
address: data.data.address,
tokensAmount: data.data.tokensAmount,
avgPrice: data.data.avgPrice,
solAmount: `${data.data.solAmount.toFixed(3)} SOL`
});
// You can update your UI here with the new trade data
this.updateTradeUI(data.data);
break;
case 'TOKEN_PRICE_UPDATE':
console.log('Token price updated:', {
tokenPrice: data.data.tokenPrice,
tokenMint: data.data.tokenMint,
tradeType: data.data.tradeType,
volume: `${data.data.volume.toFixed(3)} SOL`
});
// You can update your UI here with the new token price
this.updateTokenPriceUI(data.data);
break;
}
}
private updateTradingStatsUI(stats: any): void {
// Example: Update DOM elements with trading statistics
// document.getElementById('bought-amount')?.textContent = `${stats.bought.toFixed(3)} SOL`;
// document.getElementById('sold-amount')?.textContent = `${stats.sold.toFixed(3)} SOL`;
// document.getElementById('net-amount')?.textContent = `${stats.net.toFixed(3)} SOL`;
// document.getElementById('trade-count')?.textContent = stats.trades.toString();
}
private updateSolPriceUI(solPrice: number): void {
// Example: Update DOM elements with SOL price
// document.getElementById('sol-price')?.textContent = `$${solPrice.toFixed(2)}`;
}
private updateTradeUI(trade: any): void {
// Example: Update DOM elements with new trade data
// const tradeElement = document.createElement('div');
// tradeElement.innerHTML = `${trade.type.toUpperCase()}: ${trade.tokensAmount} tokens at $${trade.avgPrice.toFixed(4)} by ${trade.address.slice(0, 8)}...`;
// document.getElementById('trades-list')?.appendChild(tradeElement);
}
private updateTokenPriceUI(priceData: any): void {
// Example: Update DOM elements with new token price
// document.getElementById('token-price')?.textContent = `$${priceData.tokenPrice.toFixed(6)}`;
// document.getElementById('last-trade-type')?.textContent = priceData.tradeType.toUpperCase();
// document.getElementById('trade-volume')?.textContent = `${priceData.volume.toFixed(3)} SOL`;
}
private sendMessage(message: IframeMessage): void {
if (!this.isReady) {
this.messageQueue.push(message);
return;
}
this.iframe.contentWindow?.postMessage(message, '*');
}
private processMessageQueue(): void {
while (this.messageQueue.length > 0) {
const message = this.messageQueue.shift();
if (message) {
this.sendMessage(message);
}
}
}
// Public API methods
addWallets(wallets: (string | Wallet)[]): void {
this.sendMessage({
type: 'ADD_WALLETS',
wallets
});
}
clearWallets(): void {
this.sendMessage({
type: 'CLEAR_WALLETS'
});
}
getCurrentWallets(): void {
this.sendMessage({
type: 'GET_WALLETS'
});
}
}
```
### 2. Usage with React Hook
```typescript
import { useEffect, useRef, useState } from 'react';
interface TradingStats {
bought: number;
sold: number;
net: number;
trades: number;
timestamp: number;
}
interface WhitelistTrade {
type: 'buy' | 'sell';
address: string;
tokensAmount: number;
avgPrice: number;
solAmount: number;
timestamp: number;
signature: string;
}
interface TokenPriceData {
tokenPrice: number;
tokenMint: string;
timestamp: number;
tradeType: 'buy' | 'sell';
volume: number;
}
interface UseTradingIframeReturn {
addWallets: (wallets: (string | Wallet)[]) => void;
clearWallets: () => void;
getCurrentWallets: () => void;
isReady: boolean;
currentWallets: WhitelistItem[];
tradingStats: TradingStats | null;
solPrice: number | null;
recentTrades: WhitelistTrade[];
tokenPrice: TokenPriceData | null;
}
export function useTradingIframe(iframeRef: React.RefObject): UseTradingIframeReturn {
const [isReady, setIsReady] = useState(false);
const [currentWallets, setCurrentWallets] = useState([]);
const [tradingStats, setTradingStats] = useState(null);
const [solPrice, setSolPrice] = useState(null);
const [recentTrades, setRecentTrades] = useState([]);
const [tokenPrice, setTokenPrice] = useState(null);
const messageQueue = useRef([]);
useEffect(() => {
const handleMessage = (event: MessageEvent) => {
if (!iframeRef.current || event.source !== iframeRef.current.contentWindow) return;
switch (event.data.type) {
case 'IFRAME_READY':
setIsReady(true);
// Process queued messages
messageQueue.current.forEach(message => {
sendMessage(message);
});
messageQueue.current = [];
break;
case 'CURRENT_WALLETS':
setCurrentWallets(event.data.wallets);
break;
case 'WHITELIST_TRADING_STATS':
setTradingStats(event.data.data);
break;
case 'SOL_PRICE_UPDATE':
setSolPrice(event.data.data.solPrice);
break;
case 'WHITELIST_TRADE':
setRecentTrades(prev => {
const newTrades = [event.data.data, ...prev];
// Keep only the last 50 trades to prevent memory issues
return newTrades.slice(0, 50);
});
break;
case 'TOKEN_PRICE_UPDATE':
setTokenPrice(event.data.data);
break;
}
};
window.addEventListener('message', handleMessage);
return () => window.removeEventListener('message', handleMessage);
}, [iframeRef]);
const sendMessage = (message: IframeMessage): void => {
if (!isReady || !iframeRef.current) {
messageQueue.current.push(message);
return;
}
iframeRef.current.contentWindow?.postMessage(message, '*');
};
const addWallets = (wallets: (string | Wallet)[]): void => {
sendMessage({ type: 'ADD_WALLETS', wallets });
};
const clearWallets = (): void => {
sendMessage({ type: 'CLEAR_WALLETS' });
};
const getCurrentWallets = (): void => {
sendMessage({ type: 'GET_WALLETS' });
};
return {
addWallets,
clearWallets,
getCurrentWallets,
isReady,
currentWallets,
tradingStats,
solPrice,
recentTrades,
tokenPrice
};
}
```
### 3. React Component Example
```typescript
import React, { useRef } from 'react';
const TradingDashboard: React.FC = () => {
const iframeRef = useRef(null);
const { addWallets, clearWallets, getCurrentWallets, isReady, tradingStats, solPrice, recentTrades, tokenPrice } = useTradingIframe(iframeRef);
const handleAddExampleWallets = (): void => {
const wallets: Wallet[] = [
{ address: 'ABCD', label: 'Whale Trader' },
{ address: 'EFGH', label: 'Smart Money' },
{ address: '1234' } // No label
];
addWallets(wallets);
};
const handleAddStringWallets = (): void => {
const wallets = [
'XYZ1:DeFi Protocol',
'XYZ2:Market Maker',
'XYZ3' // No label
];
addWallets(wallets);
};
return (
Trading App Controls
Status: {isReady ? 'Ready' : 'Loading...'}
Add Example Wallets (Objects)
Add Example Wallets (Strings)
Get Current Wallets
Clear Wallets
{/* SOL Price Display */}
{solPrice && (
Real-time SOL Price
${solPrice.toFixed(2)}
)}
{/* Trading Statistics Display */}
{tradingStats && (
Whitelist Trading Statistics
Bought:
{tradingStats.bought.toFixed(3)} SOL
Sold:
{tradingStats.sold.toFixed(3)} SOL
Net:
= 0 ? '#22C55E' : '#EF4444' }}>
{tradingStats.net.toFixed(3)} SOL
Trades:
{tradingStats.trades}
Last updated: {new Date(tradingStats.timestamp).toLocaleTimeString()}
)}
{/* Recent Trades Display */}
{recentTrades.length > 0 && (
Recent Whitelist Trades
{recentTrades.map((trade, index) => (
{trade.type}
{trade.tokensAmount.toLocaleString()} tokens @ ${trade.avgPrice.toFixed(4)}
{new Date(trade.timestamp).toLocaleTimeString()}
Trader: {trade.address.slice(0, 8)}...{trade.address.slice(-4)} |
SOL Amount: {trade.solAmount.toFixed(3)}
))}
)}
);
};
export default TradingDashboard;
```
### 4. Advanced Example with Error Handling
```typescript
class TradingAppManager {
private iframe: HTMLIFrameElement;
private isReady: boolean = false;
private timeout: number = 10000; // 10 seconds
constructor(iframeId: string) {
this.iframe = document.getElementById(iframeId) as HTMLIFrameElement;
this.setupMessageListener();
}
async addWallets(wallets: (string | Wallet)[]): Promise {
return new Promise((resolve, reject) => {
if (!this.isReady) {
reject(new Error('Iframe not ready'));
return;
}
const timeout = setTimeout(() => {
reject(new Error('Timeout waiting for response'));
}, this.timeout);
const handleResponse = (event: MessageEvent) => {
if (event.source !== this.iframe.contentWindow) return;
if (event.data.type === 'WALLETS_ADDED') {
clearTimeout(timeout);
window.removeEventListener('message', handleResponse);
resolve(event.data.success);
}
};
window.addEventListener('message', handleResponse);
this.iframe.contentWindow?.postMessage({
type: 'ADD_WALLETS',
wallets
}, '*');
});
}
async getCurrentWallets(): Promise {
return new Promise((resolve, reject) => {
if (!this.isReady) {
reject(new Error('Iframe not ready'));
return;
}
const timeout = setTimeout(() => {
reject(new Error('Timeout waiting for response'));
}, this.timeout);
const handleResponse = (event: MessageEvent) => {
if (event.source !== this.iframe.contentWindow) return;
if (event.data.type === 'CURRENT_WALLETS') {
clearTimeout(timeout);
window.removeEventListener('message', handleResponse);
resolve(event.data.wallets);
}
};
window.addEventListener('message', handleResponse);
this.iframe.contentWindow?.postMessage({
type: 'GET_WALLETS'
}, '*');
});
}
}
```
## Security Considerations
### 1. Origin Validation
In production, validate the origin of messages:
```typescript
window.addEventListener('message', (event: MessageEvent) => {
// Validate origin
if (event.origin !== 'https://your-trading-app-domain.com') {
console.warn('Received message from untrusted origin:', event.origin);
return;
}
// Process message
handleMessage(event.data);
});
```
### 2. Content Security Policy
Add CSP headers to allow iframe embedding:
```
Content-Security-Policy: frame-ancestors 'self' https://trusted-domain.com;
```
### 3. Input Validation
Validate wallet addresses before sending:
```typescript
function isValidSolanaAddress(address: string): boolean {
// Basic Solana address validation
return /^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(address);
}
function validateWallets(wallets: (string | Wallet)[]): boolean {
return wallets.every(wallet => {
const address = typeof wallet === 'string'
? wallet.split(':')[0]
: wallet.address;
return address && address.length >= 3; // Allow partial addresses
});
}
```
## Error Handling
```typescript
interface ErrorResponse {
type: 'ERROR';
message: string;
code?: string;
}
// Enhanced message handler with error handling
function handleMessage(data: IframeResponse | ErrorResponse): void {
if (data.type === 'ERROR') {
console.error('Iframe error:', data.message);
return;
}
// Handle normal responses
switch (data.type) {
case 'IFRAME_READY':
console.log('Iframe ready for communication');
break;
// ... other cases
}
}
```
## Best Practices
1. **Wait for Ready Signal**: Always wait for `IFRAME_READY` before sending commands
2. **Queue Messages**: Queue messages if iframe isn't ready yet
3. **Validate Input**: Validate wallet addresses before sending
4. **Handle Timeouts**: Implement timeouts for async operations
5. **Error Handling**: Always handle potential errors and edge cases
6. **Type Safety**: Use TypeScript types for better development experience
7. **Security**: Validate message origins in production
## Troubleshooting
### Common Issues
1. **Messages not received**: Check if iframe is fully loaded
2. **CORS errors**: Ensure proper CORS configuration
3. **Type errors**: Verify message structure matches interfaces
4. **Timeout errors**: Increase timeout or check network connectivity
### Debug Mode
```typescript
const DEBUG = true;
function debugLog(message: string, data?: any): void {
if (DEBUG) {
console.log(`[TradingIframe] ${message}`, data);
}
}
```
This comprehensive guide should help you integrate the trading app iframe with full TypeScript support and proper error handling.