An open API service indexing awesome lists of open source software.

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

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.