diff --git a/examples/swapper-demo/README.md b/examples/swapper-demo/README.md
new file mode 100644
index 00000000..8cd871b0
--- /dev/null
+++ b/examples/swapper-demo/README.md
@@ -0,0 +1,157 @@
+# SwapperSDK Demo App
+
+A React application demonstrating the SwapperSDK capabilities with cross-chain swaps and bridge functionality using the Phantom React SDK.
+
+## Features
+
+- **Phantom React SDK Integration**: Connect using injected wallet mode with React hooks
+- **Multi-network Support**: View addresses for Solana, Ethereum, Bitcoin
+- **Quote Results Display**: Shows successful quote retrievals from SwapperSDK
+- **Preconfigured Swaps**:
+ - SOL ↔ USDC swaps on Solana
+ - Customizable amounts
+- **Cross-chain Bridges**:
+ - USDC bridging between Ethereum ↔ Solana
+ - Real-time bridge quote fetching
+- **Responsive UI**: Sidebar layout with live quote display
+
+## Prerequisites
+
+- Node.js (v16 or higher)
+- Yarn workspace setup
+- Phantom Browser Extension installed
+
+## Installation
+
+From the root of the wallet-sdk monorepo:
+
+```bash
+# Install dependencies
+yarn install
+
+# Navigate to the demo
+cd packages/swapper-sdk/examples/swapper-demo
+
+# Start development server
+yarn dev
+```
+
+The app will be available at `http://localhost:5173`
+
+## Usage
+
+### 1. Connect Wallet
+- Click "Connect Phantom" to connect your wallet
+- The sidebar will show all your wallet addresses across different networks
+
+### 2. Perform Swaps
+- **USDC to SOL**: Enter USDC amount and click swap button
+- **SOL to USDC**: Enter SOL amount and click swap button
+- View real-time quotes in the main content area
+
+### 3. Cross-chain Bridges
+- Requires both Ethereum and Solana addresses
+- **ETH → SOL**: Bridge USDC from Ethereum to Solana
+- **SOL → ETH**: Bridge USDC from Solana to Ethereum
+
+### 4. Live Quote Streaming
+- Automatically streams USDC/SOL quotes when connected
+- Shows provider, amounts, price impact, and slippage
+- Updates in real-time with latest market data
+
+## Architecture
+
+```
+src/
+├── components/
+│ ├── WalletSidebar.tsx # Left sidebar with wallet info
+│ ├── SwapperControls.tsx # Swap & bridge controls
+│ └── QuoteStreaming.tsx # Quote results display
+├── App.tsx # Main layout & React SDK config
+├── App.css # Component styling
+└── main.tsx # React entry point
+```
+
+## Key Components
+
+### React SDK Integration
+- Uses @phantom/react-sdk PhantomProvider and hooks
+- Configured for injected wallet mode only
+- Handles wallet connection state automatically
+
+### SwapperControls
+- Preconfigured swap buttons (SOL ↔ USDC)
+- Bridge functionality (ETH ↔ SOL USDC)
+- Integration with SwapperSDK for quotes
+
+### QuoteStreaming
+- Real-time quote streaming using SwapperSDK
+- Displays live market data
+- Shows quote history with timestamps
+
+## Configuration
+
+The app uses production API endpoints:
+- **SwapperSDK**: `https://api.phantom.app`
+- **Debug Mode**: Enabled for development
+
+To modify:
+```typescript
+const swapperSDK = new SwapperSDK({
+ apiUrl: 'https://api.phantom.app',
+ options: { debug: true }
+})
+```
+
+## Development
+
+### Building
+```bash
+yarn build
+```
+
+### Linting
+```bash
+yarn lint
+```
+
+### Preview Production Build
+```bash
+yarn preview
+```
+
+## Token Support
+
+The demo includes preconfigured tokens from `@phantom/swapper-sdk`:
+- **Solana**: SOL, USDC, USDT, WSOL
+- **Ethereum**: ETH, USDC, USDT, WETH
+- **Base**: ETH, USDC, WETH
+- **Polygon**: MATIC, USDC, USDT, WETH
+- **Arbitrum**: ETH, USDC, USDT, WETH
+
+## Troubleshooting
+
+### Wallet Connection Issues
+- Ensure Phantom extension is installed and unlocked
+- Check browser console for connection errors
+- Try refreshing the page
+
+### Quote Streaming Problems
+- Verify wallet is connected with Solana address
+- Check network connectivity
+- Look for API errors in browser console
+
+### Bridge Requirements
+- Both Ethereum and Solana addresses needed
+- Sufficient token balances required
+- Network fees apply for cross-chain transactions
+
+## Next Steps
+
+This demo provides a foundation for:
+- Custom swap interfaces
+- Multi-chain DeFi applications
+- Real-time price monitoring
+- Cross-chain bridge aggregation
+
+Extend the codebase by adding more token pairs, custom slippage controls, transaction history, or additional network support.
\ No newline at end of file
diff --git a/examples/swapper-demo/index.html b/examples/swapper-demo/index.html
new file mode 100644
index 00000000..b2c76754
--- /dev/null
+++ b/examples/swapper-demo/index.html
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+ SwapperSDK Demo
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/swapper-demo/package.json b/examples/swapper-demo/package.json
new file mode 100644
index 00000000..0414ace9
--- /dev/null
+++ b/examples/swapper-demo/package.json
@@ -0,0 +1,35 @@
+{
+ "name": "swapper-demo",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "predev": "yarn workspace @phantom/swapper-sdk build",
+ "dev": "vite",
+ "prebuild": "yarn workspace @phantom/swapper-sdk build",
+ "build": "tsc && vite build",
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "@phantom/react-sdk": "workspace:^",
+ "@phantom/swapper-sdk": "workspace:^",
+ "@solana/web3.js": "^1.98.4",
+ "bs58": "^6.0.0",
+ "buffer": "^6.0.3",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0"
+ },
+ "devDependencies": {
+ "@types/react": "^18.2.66",
+ "@types/react-dom": "^18.2.22",
+ "@typescript-eslint/eslint-plugin": "^7.2.0",
+ "@typescript-eslint/parser": "^7.2.0",
+ "@vitejs/plugin-react": "^4.2.1",
+ "eslint": "^8.57.0",
+ "eslint-plugin-react-hooks": "^4.6.0",
+ "eslint-plugin-react-refresh": "^0.4.6",
+ "typescript": "^5.2.2",
+ "vite": "^5.2.0"
+ }
+}
diff --git a/examples/swapper-demo/src/App.css b/examples/swapper-demo/src/App.css
new file mode 100644
index 00000000..d15c4e41
--- /dev/null
+++ b/examples/swapper-demo/src/App.css
@@ -0,0 +1,511 @@
+.app {
+ display: flex;
+ height: 100vh;
+}
+
+.sidebar {
+ width: 350px;
+ background-color: #1a1a1a;
+ border-right: 1px solid #333;
+ padding: 1.5rem;
+ overflow-y: auto;
+}
+
+.main-content {
+ flex: 1;
+ padding: 2rem;
+ overflow-y: auto;
+}
+
+.header {
+ margin-bottom: 2rem;
+}
+
+.header h1 {
+ margin: 0;
+ color: #646cff;
+ font-size: 2.5rem;
+}
+
+.header p {
+ margin: 0.5rem 0 0 0;
+ color: #888;
+ font-size: 1.1rem;
+}
+
+.connect-section {
+ text-align: center;
+ padding: 2rem 0;
+}
+
+.connect-button {
+ background: linear-gradient(135deg, #646cff 0%, #747bff 100%);
+ border: none;
+ padding: 1rem 2rem;
+ font-size: 1.1rem;
+ border-radius: 12px;
+ cursor: pointer;
+ color: white;
+ font-weight: 600;
+ transition: all 0.2s ease;
+}
+
+.connect-button:hover:not(:disabled) {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 25px rgba(116, 123, 255, 0.3);
+}
+
+.wallet-info h3 {
+ margin: 0 0 1rem 0;
+ color: #646cff;
+}
+
+.addresses-list {
+ margin-bottom: 2rem;
+}
+
+.address-item {
+ background: #2a2a2a;
+ padding: 1rem;
+ border-radius: 8px;
+ margin-bottom: 0.5rem;
+ border: 1px solid #333;
+}
+
+.address-item h4 {
+ margin: 0 0 0.5rem 0;
+ color: #646cff;
+ font-size: 0.9rem;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.address-item p {
+ margin: 0;
+ font-family: monospace;
+ font-size: 0.85rem;
+ color: #ccc;
+ word-break: break-all;
+}
+
+.swapper-section h3 {
+ margin: 2rem 0 1rem 0;
+ color: #646cff;
+ border-top: 1px solid #333;
+ padding-top: 2rem;
+}
+
+.swap-controls {
+ margin-bottom: 2rem;
+}
+
+.swap-input-group {
+ margin-bottom: 1rem;
+}
+
+.swap-input-group label {
+ display: block;
+ margin-bottom: 0.5rem;
+ color: #ccc;
+ font-size: 0.9rem;
+}
+
+.swap-input-group input {
+ width: 100%;
+ margin-bottom: 0.5rem;
+}
+
+.swap-buttons {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.swap-button {
+ background: linear-gradient(135deg, #4ecdc4 0%, #44a08d 100%);
+ border: none;
+ padding: 0.8rem;
+ border-radius: 8px;
+ color: white;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.swap-button:hover:not(:disabled) {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 15px rgba(78, 205, 196, 0.3);
+}
+
+.bridge-section h4 {
+ margin: 2rem 0 1rem 0;
+ color: #ff7b7b;
+ font-size: 1rem;
+}
+
+.bridge-buttons {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.bridge-button {
+ background: linear-gradient(135deg, #ff7b7b 0%, #ff6b6b 100%);
+ border: none;
+ padding: 0.8rem;
+ border-radius: 8px;
+ color: white;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.bridge-button:hover:not(:disabled) {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 15px rgba(255, 107, 107, 0.3);
+}
+
+.quote-container {
+ background: #1a1a1a;
+ border-radius: 12px;
+ border: 1px solid #333;
+ padding: 1.5rem;
+}
+
+.quote-container h2 {
+ margin: 0 0 1rem 0;
+ color: #646cff;
+}
+
+.quotes-list {
+ max-height: 500px;
+ overflow-y: auto;
+}
+
+.quote-item {
+ background: #2a2a2a;
+ border: 1px solid #444;
+ border-radius: 8px;
+ padding: 1rem;
+ margin-bottom: 1rem;
+}
+
+.quote-item h4 {
+ margin: 0 0 0.5rem 0;
+ color: #4ecdc4;
+}
+
+.quote-item p {
+ margin: 0.25rem 0;
+ font-size: 0.9rem;
+ color: #ccc;
+}
+
+.quote-item strong {
+ color: #fff;
+}
+
+.loading {
+ text-align: center;
+ color: #888;
+ font-style: italic;
+}
+
+.error-message {
+ background: #2d1b1b;
+ border: 1px solid #ff6b6b;
+ color: #ff6b6b;
+ padding: 1rem;
+ border-radius: 8px;
+ margin: 1rem 0;
+}
+
+/* Quote components styles */
+.quotes-list-container {
+ background: #1a1a1a;
+ border-radius: 12px;
+ border: 1px solid #333;
+ padding: 1.5rem;
+}
+
+.quotes-header {
+ margin-bottom: 2rem;
+ border-bottom: 1px solid #333;
+ padding-bottom: 1rem;
+}
+
+.quotes-header h2 {
+ margin: 0 0 0.5rem 0;
+ color: #646cff;
+ font-size: 1.8rem;
+}
+
+.swap-summary p {
+ margin: 0.25rem 0;
+ color: #ccc;
+ font-size: 1rem;
+}
+
+.swap-amount {
+ font-weight: bold;
+ color: #4ecdc4 !important;
+}
+
+.quotes-list {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ margin-bottom: 2rem;
+}
+
+.quote-card {
+ background: #2a2a2a;
+ border: 2px solid #444;
+ border-radius: 12px;
+ padding: 1.5rem;
+ transition: all 0.2s ease;
+ position: relative;
+}
+
+.quote-card:hover {
+ border-color: #646cff;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 20px rgba(100, 108, 255, 0.1);
+}
+
+.quote-card-best {
+ border-color: #4ecdc4;
+ background: linear-gradient(135deg, #2a2a2a 0%, #2d3d3a 100%);
+}
+
+.quote-card-best::before {
+ content: "BEST RATE";
+ position: absolute;
+ top: -8px;
+ right: 16px;
+ background: #4ecdc4;
+ color: #1a1a1a;
+ padding: 4px 12px;
+ font-size: 0.7rem;
+ font-weight: bold;
+ border-radius: 4px;
+ letter-spacing: 0.5px;
+}
+
+.quote-card-selected {
+ border-color: #ff7b7b;
+ background: linear-gradient(135deg, #2a2a2a 0%, #3d2d2d 100%);
+}
+
+.quote-provider {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1rem;
+}
+
+.quote-provider h4 {
+ margin: 0;
+ color: #4ecdc4;
+ font-size: 1.1rem;
+ font-weight: 600;
+}
+
+.quote-provider-badge {
+ background: #333;
+ color: #ccc;
+ padding: 0.25rem 0.5rem;
+ border-radius: 4px;
+ font-size: 0.7rem;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.quote-details {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 1rem;
+ margin-bottom: 1.5rem;
+}
+
+.quote-amounts {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.quote-amounts .sell-amount,
+.quote-amounts .buy-amount {
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+}
+
+.quote-amounts .label {
+ font-size: 0.8rem;
+ color: #888;
+ font-weight: 500;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.quote-amounts .amount {
+ font-size: 1rem;
+ font-weight: 600;
+ font-family: monospace;
+}
+
+.sell-amount .amount {
+ color: #ff7b7b;
+}
+
+.buy-amount .amount {
+ color: #4ecdc4;
+}
+
+.quote-info p {
+ margin: 0.25rem 0;
+ font-size: 0.85rem;
+ color: #888;
+}
+
+.quote-actions {
+ display: flex;
+ gap: 1rem;
+ align-items: center;
+}
+
+.execute-button {
+ background: linear-gradient(135deg, #646cff 0%, #747bff 100%);
+ border: none;
+ padding: 0.8rem 1.5rem;
+ border-radius: 8px;
+ color: white;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ flex: 1;
+}
+
+.execute-button:hover:not(:disabled) {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 15px rgba(116, 123, 255, 0.3);
+}
+
+.execute-button:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ transform: none;
+}
+
+.quote-debug {
+ background: #1a1a1a;
+ border: 1px solid #333;
+ border-radius: 8px;
+ padding: 1rem;
+ margin-top: 1rem;
+}
+
+.quote-debug h5 {
+ margin: 0 0 0.5rem 0;
+ color: #888;
+ font-size: 0.8rem;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.quote-debug pre {
+ background: #111;
+ border: 1px solid #222;
+ border-radius: 4px;
+ padding: 0.5rem;
+ margin: 0;
+ font-size: 0.75rem;
+ color: #ccc;
+ overflow-x: auto;
+ white-space: pre-wrap;
+ word-break: break-all;
+}
+
+.quotes-info {
+ text-align: center;
+ padding-top: 1rem;
+ border-top: 1px solid #333;
+}
+
+.quotes-info p {
+ margin: 0;
+ color: #888;
+ font-size: 0.9rem;
+}
+
+.quotes-empty {
+ text-align: center;
+ padding: 3rem 2rem;
+ color: #888;
+}
+
+.quotes-empty h3 {
+ margin: 0 0 1rem 0;
+ color: #646cff;
+}
+
+.quotes-empty p {
+ margin: 0;
+ font-size: 1.1rem;
+}
+
+/* Success and error cards */
+.card.success {
+ background: linear-gradient(135deg, #1b3d1b 0%, #2d4a2d 100%);
+ border: 1px solid #4ecdc4;
+ color: #4ecdc4;
+ padding: 1rem;
+ border-radius: 8px;
+ margin-bottom: 1rem;
+}
+
+.card.error {
+ background: linear-gradient(135deg, #3d1b1b 0%, #4a2d2d 100%);
+ border: 1px solid #ff7b7b;
+ color: #ff7b7b;
+ padding: 1rem;
+ border-radius: 8px;
+ margin-bottom: 1rem;
+}
+
+.card h3 {
+ margin: 0 0 0.5rem 0;
+ font-size: 1rem;
+}
+
+.card p {
+ margin: 0;
+ font-size: 0.9rem;
+}
+
+.wallet-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 1.5rem;
+}
+
+@media (max-width: 768px) {
+ .app {
+ flex-direction: column;
+ }
+
+ .sidebar {
+ width: 100%;
+ height: auto;
+ }
+
+ .quote-details {
+ grid-template-columns: 1fr;
+ }
+
+ .quote-actions {
+ flex-direction: column;
+ }
+}
\ No newline at end of file
diff --git a/examples/swapper-demo/src/App.tsx b/examples/swapper-demo/src/App.tsx
new file mode 100644
index 00000000..00f41e21
--- /dev/null
+++ b/examples/swapper-demo/src/App.tsx
@@ -0,0 +1,87 @@
+import { useState } from 'react'
+import { PhantomProvider, AddressType, type PhantomSDKConfig } from '@phantom/react-sdk'
+import { WalletSidebar } from './components/WalletSidebar'
+import { QuoteStreaming } from './components/QuoteStreaming'
+import type { SwapperSolanaQuoteRepresentation, SwapperEvmQuoteRepresentation, SwapperXChainQuoteRepresentation } from '@phantom/swapper-sdk'
+import './App.css'
+
+type Quote = SwapperSolanaQuoteRepresentation | SwapperEvmQuoteRepresentation | SwapperXChainQuoteRepresentation
+
+interface QuotesData {
+ quotes: Quote[]
+ swapInfo: {
+ sellToken: string
+ buyToken: string
+ sellAmount: string
+ }
+ type?: string
+}
+
+// Configuration for injected provider (Phantom extension)
+const config: PhantomSDKConfig = {
+ appName: "SwapperSDK Demo",
+ providerType: "injected", // Use injected mode only as requested
+ addressTypes: [AddressType.solana, AddressType.ethereum, AddressType.bitcoinSegwit],
+
+ // Enable debug for development
+ debug: {
+ enabled: true,
+ },
+}
+
+function App() {
+ const [quotesData, setQuotesData] = useState(null)
+ const [transactionStatus, setTransactionStatus] = useState<{
+ type: 'success' | 'error' | null
+ message: string
+ }>({ type: null, message: '' })
+
+ const handleQuotesReceived = (quotes: Quote[], swapInfo: QuotesData['swapInfo'], quotesType?: string) => {
+ setQuotesData({ quotes, swapInfo, type: quotesType })
+ setTransactionStatus({ type: null, message: '' }) // Clear previous status
+ }
+
+ const handleTransactionComplete = (txHash: string) => {
+ setTransactionStatus({
+ type: 'success',
+ message: `Transaction successful! Hash: ${txHash.substring(0, 10)}...`
+ })
+ }
+
+ const handleTransactionError = (error: string) => {
+ setTransactionStatus({
+ type: 'error',
+ message: error
+ })
+ }
+
+ const clearQuotes = () => {
+ setQuotesData(null)
+ setTransactionStatus({ type: null, message: '' })
+ }
+
+ return (
+
+
+
+
+
+
SwapperSDK Demo
+
Get quotes and execute swaps with transaction signing
+
+
+
+
+
+ )
+}
+
+export default App
\ No newline at end of file
diff --git a/examples/swapper-demo/src/components/QuoteCard.tsx b/examples/swapper-demo/src/components/QuoteCard.tsx
new file mode 100644
index 00000000..ca02e2d7
--- /dev/null
+++ b/examples/swapper-demo/src/components/QuoteCard.tsx
@@ -0,0 +1,174 @@
+import React from 'react'
+import type { SwapperSolanaQuoteRepresentation, SwapperEvmQuoteRepresentation, SwapperXChainQuoteRepresentation } from '@phantom/swapper-sdk'
+
+type Quote = SwapperSolanaQuoteRepresentation | SwapperEvmQuoteRepresentation | SwapperXChainQuoteRepresentation
+
+interface QuoteCardProps {
+ quote: Quote
+ isFirst: boolean
+ onSelect: (quote: Quote) => void
+ isExecuting: boolean
+ selectedQuoteId?: string
+ swapInfo?: {
+ sellToken: string
+ buyToken: string
+ sellAmount: string
+ }
+ quotesType?: string
+}
+
+export const QuoteCard: React.FC = ({
+ quote,
+ isFirst,
+ onSelect,
+ isExecuting,
+ selectedQuoteId,
+ swapInfo,
+ quotesType
+}) => {
+ const formatAmount = (amount: string, decimals: number, symbol: string) => {
+ const value = parseFloat(amount) / Math.pow(10, decimals)
+ return `${value.toFixed(decimals === 9 ? 4 : decimals === 6 ? 2 : 2)} ${symbol}`
+ }
+
+ // Determine token info based on swap direction
+ const getTokenInfo = (tokenSymbol: string) => {
+ switch (tokenSymbol.toUpperCase()) {
+ case 'SOL':
+ return { decimals: 9, symbol: 'SOL' }
+ case 'USDC':
+ return { decimals: 6, symbol: 'USDC' }
+ default:
+ // Default to 6 decimals for unknown tokens
+ return { decimals: 6, symbol: tokenSymbol }
+ }
+ }
+
+ const formatPercentage = (value: number) => {
+ return `${(value * 100).toFixed(2)}%`
+ }
+
+ const getQuoteType = () => {
+ // Use the quotesType from the response
+ if (quotesType) {
+ switch (quotesType) {
+ case 'solana':
+ return 'Solana'
+ case 'eip155':
+ return 'EVM'
+ case 'xchain':
+ return 'Cross-Chain'
+ case 'sui':
+ return 'Sui'
+ default:
+ return 'Unknown'
+ }
+ }
+
+ // Fallback to old logic
+ if ('transactionData' in quote) {
+ return Array.isArray(quote.transactionData) ? 'Solana' : 'EVM'
+ } else if ('steps' in quote) {
+ return 'Cross-Chain'
+ }
+ return 'Unknown'
+ }
+
+ const getProviderName = () => {
+ return quote.baseProvider?.name || quote.baseProvider?.id || 'Unknown Provider'
+ }
+
+ const isSelected = selectedQuoteId === quote.baseProvider?.id
+
+ return (
+
+
+
+
{getProviderName()}
+ {getQuoteType()}
+ {isFirst && Best Quote}
+
+
+
+ You Pay:
+
+ {swapInfo ? (
+ formatAmount(quote.sellAmount, getTokenInfo(swapInfo.sellToken).decimals, getTokenInfo(swapInfo.sellToken).symbol)
+ ) : (
+ formatAmount(quote.sellAmount, 6, 'USDC')
+ )}
+
+
+
+ You Get:
+
+ {swapInfo ? (
+ formatAmount(quote.buyAmount, getTokenInfo(swapInfo.buyToken).decimals, getTokenInfo(swapInfo.buyToken).symbol)
+ ) : (
+ formatAmount(quote.buyAmount, 9, 'SOL')
+ )}
+
+
+
+
+
+
+ {quote.priceImpact !== undefined && (
+
+ Price Impact:
+ 0.01 ? 'negative' : ''}>{formatPercentage(quote.priceImpact)}
+
+ )}
+
+ {quote.slippageTolerance !== undefined && (
+
+ Slippage:
+ {formatPercentage(quote.slippageTolerance)}
+
+ )}
+
+ {/* EVM specific details */}
+ {'gas' in quote && (
+
+ Gas:
+ {quote.gas?.toLocaleString() || 'N/A'}
+
+ )}
+
+ {/* Cross-chain specific details */}
+ {'executionDuration' in quote && quote.executionDuration && (
+
+ Est. Time:
+ {Math.round(quote.executionDuration / 60)} min
+
+ )}
+
+ {/* Fees information */}
+ {quote.fees && quote.fees.length > 0 && (
+
+ Fees:
+ {quote.fees.length} fee(s)
+
+ )}
+
+
+
+
+
+
+ {/* Debug information */}
+
+
+ Quote Details
+ {JSON.stringify(quote, null, 2)}
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/examples/swapper-demo/src/components/QuoteStreaming.tsx b/examples/swapper-demo/src/components/QuoteStreaming.tsx
new file mode 100644
index 00000000..06503768
--- /dev/null
+++ b/examples/swapper-demo/src/components/QuoteStreaming.tsx
@@ -0,0 +1,123 @@
+import React from 'react'
+import { usePhantom, useAccounts, AddressType } from '@phantom/react-sdk'
+import { QuotesList } from './QuotesList'
+import type { SwapperSolanaQuoteRepresentation, SwapperEvmQuoteRepresentation, SwapperXChainQuoteRepresentation } from '@phantom/react-sdk'
+
+type Quote = SwapperSolanaQuoteRepresentation | SwapperEvmQuoteRepresentation | SwapperXChainQuoteRepresentation
+
+interface QuotesData {
+ quotes: Quote[]
+ swapInfo: {
+ sellToken: string
+ buyToken: string
+ sellAmount: string
+ }
+ type?: string
+}
+
+interface QuoteStreamingProps {
+ quotesData: QuotesData | null
+ transactionStatus: {
+ type: 'success' | 'error' | null
+ message: string
+ }
+ onTransactionComplete: (txHash: string) => void
+ onTransactionError: (error: string) => void
+}
+
+export const QuoteStreaming: React.FC = ({
+ quotesData,
+ transactionStatus,
+ onTransactionComplete,
+ onTransactionError
+}) => {
+ const { isConnected } = usePhantom()
+ const addresses = useAccounts() || []
+
+ const solanaAddress = addresses.find(addr => addr.addressType === AddressType.solana)?.address
+ const ethereumAddress = addresses.find(addr => addr.addressType === AddressType.ethereum)?.address
+
+ const formatAddress = (address: string) => {
+ if (!address) return 'Not available'
+ return `${address.slice(0, 8)}...${address.slice(-8)}`
+ }
+
+ return (
+
+ {/* Transaction Status */}
+ {transactionStatus.type && (
+
+
{transactionStatus.type === 'success' ? '✅ Transaction Successful' : '❌ Transaction Failed'}
+
{transactionStatus.message}
+
+ )}
+
+ {/* Quotes Display */}
+ {quotesData ? (
+
+ ) : (
+ <>
+
SwapperSDK Transaction Demo
+
+ {!isConnected ? (
+
+ Connect your wallet to see quotes and execute swaps
+
+ ) : (
+
+
Ready for Live Trading
+
Use the controls in the sidebar to:
+
+ - Get Real Quotes: Fetch live prices from DEXs and aggregators
+ - Execute Swaps: Sign and submit transactions directly from quotes
+ - Cross-Chain Bridge: Bridge USDC between Ethereum ↔ Solana
+
+
+
+
Your Connected Addresses:
+ {solanaAddress && (
+
Solana: {formatAddress(solanaAddress)}
+ )}
+ {ethereumAddress && (
+
Ethereum: {formatAddress(ethereumAddress)}
+ )}
+
+
+ )}
+
+
+
SwapperSDK + React SDK Integration
+
+
+
Swap Features
+
+ - Live quote comparison
+ - Multiple DEX aggregators
+ - Price impact calculation
+ - Slippage protection
+ - Gas estimation
+
+
+
+
Transaction Flow
+
+ - 1. Click swap button
+ - 2. Review quotes here
+ - 3. Click "Execute Swap"
+ - 4. Sign with Phantom
+ - 5. Transaction complete!
+
+
+
+
+ >
+ )}
+
+ )
+}
\ No newline at end of file
diff --git a/examples/swapper-demo/src/components/QuotesList.tsx b/examples/swapper-demo/src/components/QuotesList.tsx
new file mode 100644
index 00000000..93db09df
--- /dev/null
+++ b/examples/swapper-demo/src/components/QuotesList.tsx
@@ -0,0 +1,178 @@
+import React, { useMemo, useState } from 'react'
+import { useSignAndSendTransaction, usePhantom, AddressType } from '@phantom/react-sdk'
+import { VersionedTransaction } from '@solana/web3.js'
+import { getSolanaTransactionFromQuote, inspectSolanaTransaction } from '@phantom/swapper-sdk'
+import { QuoteCard } from './QuoteCard'
+import type { SwapperSolanaQuoteRepresentation, SwapperEvmQuoteRepresentation, SwapperXChainQuoteRepresentation } from '@phantom/swapper-sdk'
+
+type Quote = SwapperSolanaQuoteRepresentation | SwapperEvmQuoteRepresentation | SwapperXChainQuoteRepresentation
+
+interface QuotesListProps {
+ quotes: Quote[]
+ swapInfo: {
+ sellToken: string
+ buyToken: string
+ sellAmount: string
+ }
+ quotesType?: string // Add the type from the quotes response
+ onTransactionComplete: (txHash: string) => void
+ onTransactionError: (error: string) => void
+}
+
+export const QuotesList: React.FC = ({
+ quotes,
+ swapInfo,
+ quotesType,
+ onTransactionComplete,
+ onTransactionError
+}) => {
+ const { addresses } = usePhantom()
+ const { signAndSendTransaction, isSigning } = useSignAndSendTransaction()
+ const [selectedQuoteId, setSelectedQuoteId] = useState(null)
+
+ const targetAddress = useMemo(() => {
+ // Get the first address that matches the quotes type
+ if (quotesType) {
+ switch (quotesType) {
+ case 'solana':
+ return addresses.find(addr => addr.addressType === AddressType.solana)?.address
+ case 'eip155':
+ return addresses.find(addr => addr.addressType === AddressType.ethereum)?.address
+ case 'xchain':
+ // For cross-chain, we need to determine based on the first step
+ // Default to Solana for now
+ return addresses.find(addr => addr.addressType === AddressType.solana)?.address
+ case 'sui':
+ // Sui not supported in this demo
+ return undefined
+ default:
+ return addresses.find(addr => addr.addressType === AddressType.solana)?.address
+ }
+ }
+ return null;
+ }, [addresses, quotesType])
+
+
+ const executeSwap = async (quote: Quote) => {
+ if (!targetAddress) {
+ onTransactionError('No compatible address found for this quote')
+ return
+ }
+
+ setSelectedQuoteId(quote.baseProvider?.id || 'unknown')
+
+ try {
+ let transactions: VersionedTransaction[]
+ let networkId: string
+
+ // Extract and parse transaction data based on quotes type
+ if (!quotesType) {
+ throw new Error('Quotes type not available')
+ }
+
+ switch (quotesType) {
+ case 'solana':
+ if (!('transactionData' in quote)) {
+ throw new Error('Invalid Solana quote: missing transaction data')
+ }
+
+ console.log('Parsing Solana transaction with SwapperSDK utility...')
+
+ // Use the SwapperSDK utility function to parse transactions
+ transactions = getSolanaTransactionFromQuote(quote as SwapperSolanaQuoteRepresentation)
+
+ // Inspect the first transaction for debugging
+ if (transactions.length > 0) {
+ inspectSolanaTransaction(transactions[0], 0)
+ }
+
+ networkId = 'solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp' // Solana mainnet
+ break
+
+ case 'eip155':
+ // EVM transactions are handled differently - they stay as string data
+ throw new Error('EVM transactions not implemented in this demo')
+
+ case 'xchain':
+ throw new Error('Multi-step cross-chain transactions are not supported in this demo')
+
+ case 'sui':
+ throw new Error('Sui transactions are not supported in this demo')
+
+ default:
+ throw new Error(`Unsupported quotes type: ${quotesType}`)
+ }
+
+ console.log('Executing swap with parsed transactions:', {
+ transactionCount: transactions.length,
+ firstTransaction: 'VersionedTransaction with ' + transactions[0].message.compiledInstructions.length + ' instructions',
+ networkId,
+ quote: quote.baseProvider
+ })
+
+ // Execute the first transaction (most swaps only have one transaction)
+ // For multi-step swaps, we'd need to execute all transactions in sequence
+ const transactionToExecute = transactions[0]
+
+ const result = await signAndSendTransaction({
+ transaction: transactionToExecute,
+ networkId: networkId as any,
+ })
+
+ console.log('Transaction completed:', result)
+ onTransactionComplete(result.rawTransaction || 'Transaction completed')
+
+ } catch (error: any) {
+ console.error('Swap execution failed:', error)
+ onTransactionError(`Swap failed: ${error.message || 'Unknown error'}`)
+ } finally {
+ setSelectedQuoteId(null)
+ }
+ }
+
+ if (!quotes || quotes.length === 0) {
+ return (
+
+
No Quotes Available
+
Use the swap controls in the sidebar to get quotes.
+
+ )
+ }
+
+ return (
+
+
+
Available Quotes
+
+
+ {swapInfo.sellToken} → {swapInfo.buyToken}
+
+
Amount: {swapInfo.sellAmount}
+
+
+
+
+ {quotes.map((quote, index) => (
+
+ ))}
+
+
+
+
+ {quotes.length} quote{quotes.length !== 1 ? 's' : ''} found •
+ Best rate is highlighted •
+ Click "Execute Swap" to proceed
+
+
+
+ )
+}
\ No newline at end of file
diff --git a/examples/swapper-demo/src/components/SwapperControls.tsx b/examples/swapper-demo/src/components/SwapperControls.tsx
new file mode 100644
index 00000000..ac4cceb8
--- /dev/null
+++ b/examples/swapper-demo/src/components/SwapperControls.tsx
@@ -0,0 +1,258 @@
+import React, { useState } from 'react'
+import SwapperSDK, { TOKENS, NetworkId } from '@phantom/swapper-sdk'
+import { usePhantom, AddressType } from '@phantom/react-sdk'
+import type { WalletAddress, SwapperSolanaQuoteRepresentation, SwapperEvmQuoteRepresentation, SwapperXChainQuoteRepresentation } from '@phantom/react-sdk'
+
+type Quote = SwapperSolanaQuoteRepresentation | SwapperEvmQuoteRepresentation | SwapperXChainQuoteRepresentation
+
+interface SwapperControlsProps {
+ addresses: WalletAddress[]
+ onQuotesReceived: (quotes: Quote[], swapInfo: {
+ sellToken: string
+ buyToken: string
+ sellAmount: string
+ }, quotesType?: string) => void
+}
+
+const swapperSDK = new SwapperSDK({
+ apiUrl: 'https://api.phantom.app',
+ options: { debug: true }
+})
+
+export const SwapperControls: React.FC = ({ addresses, onQuotesReceived }) => {
+ const { sdk } = usePhantom()
+ const [usdcAmount, setUsdcAmount] = useState('10')
+ const [solAmount, setSolAmount] = useState('0.1')
+ const [isSwapping, setIsSwapping] = useState(false)
+ const [bridgeAmount, setBridgeAmount] = useState('5')
+ const [lastResult, setLastResult] = useState(null)
+
+ // Get addresses for specific networks (addresses prop is already safe from parent)
+ const safeAddresses = addresses || []
+ const solanaAddress = safeAddresses.find(addr => addr.addressType === AddressType.solana)?.address
+ const ethereumAddress = safeAddresses.find(addr => addr.addressType === AddressType.ethereum)?.address
+
+ const handleSwap = async (swapType: 'usdc-to-sol' | 'sol-to-usdc') => {
+ if (!sdk || !solanaAddress) {
+ setLastResult('Error: Solana address not found')
+ return
+ }
+
+ setIsSwapping(true)
+ setLastResult(null)
+
+ try {
+ let sellToken, buyToken, sellAmount, displayPair
+
+ if (swapType === 'usdc-to-sol') {
+ sellToken = TOKENS.SOLANA_MAINNET.USDC
+ buyToken = TOKENS.SOLANA_MAINNET.SOL
+ sellAmount = String(parseFloat(usdcAmount) * 1_000_000) // USDC has 6 decimals
+ displayPair = { sell: 'USDC', buy: 'SOL' }
+ } else {
+ sellToken = TOKENS.SOLANA_MAINNET.SOL
+ buyToken = TOKENS.SOLANA_MAINNET.USDC
+ sellAmount = String(parseFloat(solAmount) * 1_000_000_000) // SOL has 9 decimals
+ displayPair = { sell: 'SOL', buy: 'USDC' }
+ }
+
+ // Get quotes
+ const quotes = await swapperSDK.getQuotes({
+ sellToken,
+ buyToken,
+ sellAmount,
+ from: {
+ address: solanaAddress,
+ networkId: NetworkId.SOLANA_MAINNET,
+ },
+ slippageTolerance: 0.5,
+ })
+
+ console.log('Swap quotes:', quotes)
+
+ if (quotes.quotes && quotes.quotes.length > 0) {
+ // Pass quotes to main view
+ onQuotesReceived(quotes.quotes as Quote[], {
+ sellToken: displayPair.sell,
+ buyToken: displayPair.buy,
+ sellAmount: swapType === 'usdc-to-sol' ? usdcAmount + ' USDC' : solAmount + ' SOL'
+ }, quotes.type)
+ setLastResult(`✅ Got ${quotes.quotes.length} quotes - check main view`)
+ } else {
+ setLastResult(`❌ No quotes available for ${displayPair.sell} → ${displayPair.buy}`)
+ }
+
+ } catch (error: any) {
+ console.error('Swap failed:', error)
+ setLastResult(`❌ Swap failed: ${error.message}`)
+ } finally {
+ setIsSwapping(false)
+ }
+ }
+
+ const handleBridge = async (bridgeType: 'eth-to-sol' | 'sol-to-eth') => {
+ if (!sdk || !solanaAddress || !ethereumAddress) {
+ setLastResult('Error: Both Solana and Ethereum addresses required for bridging')
+ return
+ }
+
+ setIsSwapping(true)
+ setLastResult(null)
+
+ try {
+ let sellToken, buyToken, sellAmount, fromAddress, fromNetwork, toAddress, toNetwork, displayPair
+
+ if (bridgeType === 'eth-to-sol') {
+ sellToken = TOKENS.ETHEREUM_MAINNET.USDC
+ buyToken = TOKENS.SOLANA_MAINNET.USDC
+ sellAmount = String(parseFloat(bridgeAmount) * 1_000_000) // USDC has 6 decimals
+ fromAddress = ethereumAddress
+ fromNetwork = NetworkId.ETHEREUM_MAINNET
+ toAddress = solanaAddress
+ toNetwork = NetworkId.SOLANA_MAINNET
+ displayPair = { sell: 'ETH-USDC', buy: 'SOL-USDC' }
+ } else {
+ sellToken = TOKENS.SOLANA_MAINNET.USDC
+ buyToken = TOKENS.ETHEREUM_MAINNET.USDC
+ sellAmount = String(parseFloat(bridgeAmount) * 1_000_000) // USDC has 6 decimals
+ fromAddress = solanaAddress
+ fromNetwork = NetworkId.SOLANA_MAINNET
+ toAddress = ethereumAddress
+ toNetwork = NetworkId.ETHEREUM_MAINNET
+ displayPair = { sell: 'SOL-USDC', buy: 'ETH-USDC' }
+ }
+
+ // Get bridge quotes
+ const quotes = await swapperSDK.getQuotes({
+ sellToken,
+ buyToken,
+ sellAmount,
+ from: {
+ address: fromAddress,
+ networkId: fromNetwork,
+ },
+ to: {
+ address: toAddress,
+ networkId: toNetwork,
+ },
+ slippageTolerance: 1.0,
+ })
+
+ console.log('Bridge quotes:', quotes)
+
+ if (quotes.quotes && quotes.quotes.length > 0) {
+ // Pass bridge quotes to main view
+ onQuotesReceived(quotes.quotes as Quote[], {
+ sellToken: displayPair.sell,
+ buyToken: displayPair.buy,
+ sellAmount: bridgeAmount + ' USDC'
+ }, quotes.type)
+ setLastResult(`✅ Got ${quotes.quotes.length} bridge quotes - check main view`)
+ } else {
+ setLastResult(`❌ No bridge quotes available for ${displayPair.sell} → ${displayPair.buy}`)
+ }
+
+ } catch (error: any) {
+ console.error('Bridge failed:', error)
+ setLastResult(`❌ Bridge failed: ${error.message}`)
+ } finally {
+ setIsSwapping(false)
+ }
+ }
+
+ return (
+
+
Swap & Bridge
+
+ {lastResult && (
+
+ {lastResult}
+
+ )}
+
+
+
Solana Swaps
+
+
+
+ setUsdcAmount(e.target.value)}
+ min="0"
+ step="0.1"
+ disabled={isSwapping}
+ />
+
+
+
+
+ setSolAmount(e.target.value)}
+ min="0"
+ step="0.01"
+ disabled={isSwapping}
+ />
+
+
+
+
+
+
+
+
+
+
Cross-Chain Bridge
+
+
+
+ setBridgeAmount(e.target.value)}
+ min="0"
+ step="0.1"
+ disabled={isSwapping}
+ />
+
+
+
+
+
+
+
+ {(!ethereumAddress || !solanaAddress) && (
+
+ Bridge requires both Ethereum and Solana addresses
+
+ )}
+
+
+ )
+}
\ No newline at end of file
diff --git a/examples/swapper-demo/src/components/WalletSidebar.tsx b/examples/swapper-demo/src/components/WalletSidebar.tsx
new file mode 100644
index 00000000..cc2d858c
--- /dev/null
+++ b/examples/swapper-demo/src/components/WalletSidebar.tsx
@@ -0,0 +1,111 @@
+import React from 'react'
+import { usePhantom, useConnect, useAccounts, useDisconnect, AddressType } from '@phantom/react-sdk'
+import { SwapperControls } from './SwapperControls'
+import type { SwapperSolanaQuoteRepresentation, SwapperEvmQuoteRepresentation, SwapperXChainQuoteRepresentation } from '@phantom/react-sdk'
+
+type Quote = SwapperSolanaQuoteRepresentation | SwapperEvmQuoteRepresentation | SwapperXChainQuoteRepresentation
+
+interface WalletSidebarProps {
+ onQuotesReceived: (quotes: Quote[], swapInfo: {
+ sellToken: string
+ buyToken: string
+ sellAmount: string
+ }, quotesType?: string) => void
+ clearQuotes: () => void
+}
+
+export const WalletSidebar: React.FC = ({ onQuotesReceived, clearQuotes }) => {
+ const { isConnected, error } = usePhantom()
+ const addresses = useAccounts() || []
+ const { connect, isConnecting } = useConnect()
+ const { disconnect } = useDisconnect()
+ console.log(isConnected, addresses, error)
+ const formatAddress = (address: string, length: number = 8) => {
+ if (!address) return ''
+ return `${address.slice(0, length)}...${address.slice(-length)}`
+ }
+
+ const getNetworkName = (addressType: any) => {
+ switch (addressType) {
+ case AddressType.solana:
+ return 'Solana'
+ case AddressType.ethereum:
+ return 'Ethereum'
+ case AddressType.bitcoinSegwit:
+ return 'Bitcoin (SegWit)'
+ default:
+ return String(addressType)
+ }
+ }
+
+ return (
+
+ {!isConnected ? (
+
+
Connect Your Wallet
+
Connect to Phantom wallet to start swapping
+
+ {error && (
+
+ {error.message}
+
+ )}
+
+ ) : (
+
+
+
Wallet Connected
+
+
+
+
+
Your Addresses:
+ {addresses.map((addr, index) => (
+
+
{getNetworkName(addr.addressType)}
+
{formatAddress(addr.address, 6)}
+
+ ))}
+
+
+
+
+
+
+
+
+ )}
+
+ )
+}
\ No newline at end of file
diff --git a/examples/swapper-demo/src/index.css b/examples/swapper-demo/src/index.css
new file mode 100644
index 00000000..b231dc14
--- /dev/null
+++ b/examples/swapper-demo/src/index.css
@@ -0,0 +1,98 @@
+:root {
+ font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+ line-height: 1.5;
+ font-weight: 400;
+
+ color-scheme: light dark;
+ color: rgba(255, 255, 255, 0.87);
+ background-color: #242424;
+
+ font-synthesis: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-text-size-adjust: 100%;
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ display: flex;
+ min-width: 320px;
+ min-height: 100vh;
+}
+
+#root {
+ width: 100%;
+ height: 100vh;
+}
+
+h1 {
+ font-size: 3.2em;
+ line-height: 1.1;
+}
+
+button {
+ border-radius: 8px;
+ border: 1px solid transparent;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ font-weight: 500;
+ font-family: inherit;
+ background-color: #1a1a1a;
+ color: white;
+ cursor: pointer;
+ transition: border-color 0.25s;
+}
+
+button:hover {
+ border-color: #646cff;
+}
+
+button:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+}
+
+input {
+ border-radius: 8px;
+ border: 1px solid #646cff;
+ padding: 0.6em 1.2em;
+ font-size: 1em;
+ background-color: #1a1a1a;
+ color: white;
+}
+
+input:focus {
+ outline: none;
+ border-color: #747bff;
+}
+
+.card {
+ padding: 1em;
+ background-color: #1a1a1a;
+ border-radius: 8px;
+ border: 1px solid #333;
+ margin: 0.5em 0;
+}
+
+.quote-card {
+ background-color: #2a2a2a;
+ border: 1px solid #444;
+ margin-bottom: 0.5em;
+}
+
+.error {
+ color: #ff6b6b;
+ background-color: #2d1b1b;
+ border-color: #ff6b6b;
+}
+
+.success {
+ color: #51cf66;
+ background-color: #1b2d1b;
+ border-color: #51cf66;
+}
\ No newline at end of file
diff --git a/examples/swapper-demo/src/main.tsx b/examples/swapper-demo/src/main.tsx
new file mode 100644
index 00000000..cbe1cdf3
--- /dev/null
+++ b/examples/swapper-demo/src/main.tsx
@@ -0,0 +1,10 @@
+import React from 'react'
+import ReactDOM from 'react-dom/client'
+import App from './App.tsx'
+import './index.css'
+
+ReactDOM.createRoot(document.getElementById('root')!).render(
+
+
+ ,
+)
\ No newline at end of file
diff --git a/examples/swapper-demo/tsconfig.json b/examples/swapper-demo/tsconfig.json
new file mode 100644
index 00000000..7a7611e4
--- /dev/null
+++ b/examples/swapper-demo/tsconfig.json
@@ -0,0 +1,25 @@
+{
+ "compilerOptions": {
+ "target": "ES2020",
+ "useDefineForClassFields": true,
+ "lib": ["ES2020", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+
+ /* Bundler mode */
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ "jsx": "react-jsx",
+
+ /* Linting */
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noFallthroughCasesInSwitch": true
+ },
+ "include": ["src"],
+ "references": [{ "path": "./tsconfig.node.json" }]
+}
\ No newline at end of file
diff --git a/examples/swapper-demo/tsconfig.node.json b/examples/swapper-demo/tsconfig.node.json
new file mode 100644
index 00000000..099658cf
--- /dev/null
+++ b/examples/swapper-demo/tsconfig.node.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "composite": true,
+ "skipLibCheck": true,
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "allowSyntheticDefaultImports": true
+ },
+ "include": ["vite.config.ts"]
+}
\ No newline at end of file
diff --git a/examples/swapper-demo/vite.config.ts b/examples/swapper-demo/vite.config.ts
new file mode 100644
index 00000000..4142f998
--- /dev/null
+++ b/examples/swapper-demo/vite.config.ts
@@ -0,0 +1,15 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+ plugins: [react()],
+ define: {
+ global: 'globalThis',
+ },
+ resolve: {
+ alias: {
+ buffer: 'buffer',
+ },
+ },
+})
\ No newline at end of file
diff --git a/packages/react-sdk/src/PhantomProvider.tsx b/packages/react-sdk/src/PhantomProvider.tsx
index dbb06d72..a06e8612 100644
--- a/packages/react-sdk/src/PhantomProvider.tsx
+++ b/packages/react-sdk/src/PhantomProvider.tsx
@@ -48,22 +48,7 @@ export function PhantomProvider({ children, config }: PhantomProviderProps) {
const [currentProviderType, setCurrentProviderType] = useState<"injected" | "embedded" | null>(null);
const [isPhantomAvailable, setIsPhantomAvailable] = useState(false);
- // Check if Phantom extension is available (only for injected provider)
- useEffect(() => {
- const checkPhantomExtension = async () => {
- try {
- const available = await sdk.waitForPhantomExtension(1000);
- setIsPhantomAvailable(available);
- } catch (err) {
- console.error("Error checking Phantom extension:", err);
- setIsPhantomAvailable(false);
- }
- };
-
- checkPhantomExtension();
- }, [sdk]);
-
- // Function to update connection state and provider info
+ // Function to update connection state and provider info
const updateConnectionState = useCallback(async () => {
try {
const connected = sdk.isConnected();
@@ -86,6 +71,24 @@ export function PhantomProvider({ children, config }: PhantomProviderProps) {
setError(err as Error);
}
}, [sdk]);
+
+ // Check if Phantom extension is available (only for injected provider)
+ useEffect(() => {
+ const checkPhantomExtension = async () => {
+ try {
+ const available = await sdk.waitForPhantomExtension(1000);
+ setIsPhantomAvailable(available);
+ } catch (err) {
+ console.error("Error checking Phantom extension:", err);
+ setIsPhantomAvailable(false);
+ }
+ };
+
+ checkPhantomExtension();
+ updateConnectionState();
+ }, [sdk, updateConnectionState]);
+
+
// Initialize connection state
useEffect(() => {
diff --git a/packages/react-sdk/src/hooks/useConnect.ts b/packages/react-sdk/src/hooks/useConnect.ts
index 8d13cfa3..e4210ec8 100644
--- a/packages/react-sdk/src/hooks/useConnect.ts
+++ b/packages/react-sdk/src/hooks/useConnect.ts
@@ -17,6 +17,7 @@ export function useConnect() {
try {
const result = await context.sdk.connect(options);
+ await context.updateConnectionState();
return result;
} catch (err) {
@@ -35,6 +36,5 @@ export function useConnect() {
isConnecting,
error,
currentProviderType: context.currentProviderType,
- isPhantomAvailable: context.isPhantomAvailable,
};
}
diff --git a/packages/swapper-sdk/package.json b/packages/swapper-sdk/package.json
index b419e572..e40936ec 100644
--- a/packages/swapper-sdk/package.json
+++ b/packages/swapper-sdk/package.json
@@ -39,6 +39,9 @@
"typescript": "^5.0.4"
},
"dependencies": {
+ "@solana/web3.js": "^1.98.4",
+ "bs58": "^6.0.0",
+ "buffer": "^6.0.3",
"eventsource": "^2.0.2"
},
"files": [
diff --git a/packages/swapper-sdk/src/index.ts b/packages/swapper-sdk/src/index.ts
index b4d5d5d9..f38d1eb2 100644
--- a/packages/swapper-sdk/src/index.ts
+++ b/packages/swapper-sdk/src/index.ts
@@ -30,4 +30,13 @@ export type {
FeeType,
} from "./types";
-export { SwapperSDK as default } from "./swapper-sdk";
\ No newline at end of file
+export { SwapperSDK as default } from "./swapper-sdk";
+
+// Transaction parsing utilities
+export {
+ getSolanaTransactionFromQuote,
+ getEvmTransactionFromQuote,
+ getXChainTransactionFromQuote,
+ transactionStringToTransaction,
+ inspectSolanaTransaction
+} from "./utils/transaction-parsers";
\ No newline at end of file
diff --git a/packages/swapper-sdk/src/utils/transaction-parsers.ts b/packages/swapper-sdk/src/utils/transaction-parsers.ts
new file mode 100644
index 00000000..fa2a1b56
--- /dev/null
+++ b/packages/swapper-sdk/src/utils/transaction-parsers.ts
@@ -0,0 +1,154 @@
+import { VersionedTransaction, Transaction } from '@solana/web3.js'
+import { Buffer } from 'buffer'
+import bs58 from 'bs58'
+import type {
+ SwapperSolanaQuoteRepresentation,
+ SwapperEvmQuoteRepresentation,
+ SwapperXChainQuoteRepresentation
+} from '../types/quotes'
+
+/**
+ * Converts a base58 encoded transaction string to a VersionedTransaction object
+ * @param transactionString - Base58 encoded serialized transaction
+ * @param encoding - Encoding format (should be "bs58" for Solana)
+ * @returns Parsed VersionedTransaction
+ */
+export function transactionStringToTransaction(
+ transactionString: string,
+ encoding: "bs58"
+): VersionedTransaction {
+ try {
+ // Step 1: Decode the base58 string to raw bytes
+ const transactionBytes = bs58.decode(transactionString)
+
+ // Step 2: Deserialize the bytes into a VersionedTransaction
+ const transaction = VersionedTransaction.deserialize(transactionBytes)
+
+ return transaction
+ } catch (error) {
+ throw new Error(`Failed to parse transaction string: ${error}`)
+ }
+}
+
+/**
+ * Parses Solana transaction data from a SwapperSolanaQuoteRepresentation
+ * @param quote - The Solana quote containing transaction data
+ * @returns Array of parsed VersionedTransaction objects
+ */
+export function getSolanaTransactionFromQuote(quote: SwapperSolanaQuoteRepresentation): VersionedTransaction[] {
+ if (!quote.transactionData || !Array.isArray(quote.transactionData)) {
+ throw new Error('Invalid Solana quote: missing or invalid transaction data')
+ }
+
+ if (quote.transactionData.length === 0) {
+ throw new Error('No transaction data in Solana quote')
+ }
+
+ const parsedTransactions: VersionedTransaction[] = []
+
+ for (const [index, txString] of quote.transactionData.entries()) {
+ try {
+ // Validate input
+ if (!txString || typeof txString !== 'string') {
+ throw new Error(`Invalid transaction string at index ${index}`)
+ }
+
+ // Parse the transaction using base58 encoding
+ const transaction = transactionStringToTransaction(txString, "bs58")
+
+ // Verify it's a valid transaction
+ if (!transaction.message || !transaction.message.staticAccountKeys) {
+ throw new Error(`Invalid transaction structure at index ${index}`)
+ }
+
+ parsedTransactions.push(transaction)
+
+ console.log(`✅ Transaction ${index} parsed successfully`)
+ console.log(` - ${transaction.message.staticAccountKeys.length} account keys`)
+ console.log(` - ${transaction.message.compiledInstructions.length} instructions`)
+
+ // Log address table lookups for debugging
+ if (transaction.message.addressTableLookups && transaction.message.addressTableLookups.length > 0) {
+ console.log(` - ${transaction.message.addressTableLookups.length} address table lookups`)
+ }
+
+ } catch (error) {
+ console.error(`❌ Failed to parse transaction ${index}:`, error)
+ throw new Error(`Failed to parse transaction ${index}: ${error}`)
+ }
+ }
+
+ return parsedTransactions
+}
+
+/**
+ * Gets EVM transaction data from a SwapperEvmQuoteRepresentation
+ * @param quote - The EVM quote containing transaction data
+ * @returns Raw transaction data string (not implemented yet)
+ */
+export function getEvmTransactionFromQuote(quote: SwapperEvmQuoteRepresentation): string {
+ if (!quote.transactionData || typeof quote.transactionData !== 'string') {
+ throw new Error('Invalid EVM quote: missing or invalid transaction data')
+ }
+
+ // For now, just return the raw transaction data
+ // TODO: Implement proper EVM transaction parsing when needed
+ return quote.transactionData
+}
+
+/**
+ * Gets cross-chain transaction data from a SwapperXChainQuoteRepresentation
+ * @param quote - The cross-chain quote containing transaction steps
+ * @returns Array of transaction step data (not implemented yet)
+ */
+export function getXChainTransactionFromQuote(quote: SwapperXChainQuoteRepresentation): string[] {
+ if (!quote.steps || !Array.isArray(quote.steps)) {
+ throw new Error('Invalid cross-chain quote: missing or invalid steps')
+ }
+
+ // For now, just return the transaction data from each step
+ // TODO: Implement proper cross-chain transaction parsing when needed
+ return quote.steps.map(step => step.transactionData)
+}
+
+/**
+ * Inspects a parsed Solana transaction and logs detailed information
+ * @param transaction - The VersionedTransaction to inspect
+ * @param index - Optional index for logging
+ */
+export function inspectSolanaTransaction(transaction: VersionedTransaction, index?: number): void {
+ const prefix = index !== undefined ? `Transaction ${index}:` : 'Transaction:'
+
+ console.log(`${prefix} Details`)
+ console.log('- Version:', transaction.version)
+ console.log('- Signatures:', transaction.signatures.map(sig => sig?.toString() || 'unsigned'))
+
+ // Message details
+ const message = transaction.message
+ console.log('Message Details:')
+ console.log('- Account Keys:', message.staticAccountKeys.map(key => key.toString()))
+ console.log('- Recent Blockhash:', message.recentBlockhash)
+ console.log('- Instructions Count:', message.compiledInstructions.length)
+
+ // Instruction details
+ message.compiledInstructions.forEach((instruction, instructionIndex) => {
+ console.log(`Instruction ${instructionIndex}:`, {
+ programIdIndex: instruction.programIdIndex,
+ accountsLength: instruction.accountKeyIndexes.length,
+ dataLength: instruction.data.length,
+ data: Buffer.from(instruction.data).toString('hex').substring(0, 32) + '...'
+ })
+ })
+
+ // Address table lookups (for versioned transactions)
+ if (message.addressTableLookups && message.addressTableLookups.length > 0) {
+ console.log('Address Table Lookups:', message.addressTableLookups.length)
+ message.addressTableLookups.forEach((lookup, lookupIndex) => {
+ console.log(`Lookup ${lookupIndex}:`, {
+ accountKey: lookup.accountKey.toString(),
+ writableIndexes: lookup.writableIndexes,
+ readonlyIndexes: lookup.readonlyIndexes
+ })
+ })
+ }
+}
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index f6f8ed9b..4b02c1a3 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1436,6 +1436,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/aix-ppc64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/aix-ppc64@npm:0.21.5"
+ conditions: os=aix & cpu=ppc64
+ languageName: node
+ linkType: hard
+
"@esbuild/aix-ppc64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/aix-ppc64@npm:0.25.8"
@@ -1457,6 +1464,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/android-arm64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/android-arm64@npm:0.21.5"
+ conditions: os=android & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/android-arm64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/android-arm64@npm:0.25.8"
@@ -1478,6 +1492,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/android-arm@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/android-arm@npm:0.21.5"
+ conditions: os=android & cpu=arm
+ languageName: node
+ linkType: hard
+
"@esbuild/android-arm@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/android-arm@npm:0.25.8"
@@ -1499,6 +1520,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/android-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/android-x64@npm:0.21.5"
+ conditions: os=android & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/android-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/android-x64@npm:0.25.8"
@@ -1520,6 +1548,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/darwin-arm64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/darwin-arm64@npm:0.21.5"
+ conditions: os=darwin & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/darwin-arm64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/darwin-arm64@npm:0.25.8"
@@ -1541,6 +1576,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/darwin-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/darwin-x64@npm:0.21.5"
+ conditions: os=darwin & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/darwin-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/darwin-x64@npm:0.25.8"
@@ -1562,6 +1604,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/freebsd-arm64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/freebsd-arm64@npm:0.21.5"
+ conditions: os=freebsd & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/freebsd-arm64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/freebsd-arm64@npm:0.25.8"
@@ -1583,6 +1632,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/freebsd-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/freebsd-x64@npm:0.21.5"
+ conditions: os=freebsd & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/freebsd-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/freebsd-x64@npm:0.25.8"
@@ -1604,6 +1660,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-arm64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-arm64@npm:0.21.5"
+ conditions: os=linux & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-arm64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-arm64@npm:0.25.8"
@@ -1625,6 +1688,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-arm@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-arm@npm:0.21.5"
+ conditions: os=linux & cpu=arm
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-arm@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-arm@npm:0.25.8"
@@ -1646,6 +1716,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-ia32@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-ia32@npm:0.21.5"
+ conditions: os=linux & cpu=ia32
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-ia32@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-ia32@npm:0.25.8"
@@ -1667,6 +1744,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-loong64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-loong64@npm:0.21.5"
+ conditions: os=linux & cpu=loong64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-loong64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-loong64@npm:0.25.8"
@@ -1688,6 +1772,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-mips64el@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-mips64el@npm:0.21.5"
+ conditions: os=linux & cpu=mips64el
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-mips64el@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-mips64el@npm:0.25.8"
@@ -1709,6 +1800,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-ppc64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-ppc64@npm:0.21.5"
+ conditions: os=linux & cpu=ppc64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-ppc64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-ppc64@npm:0.25.8"
@@ -1730,6 +1828,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-riscv64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-riscv64@npm:0.21.5"
+ conditions: os=linux & cpu=riscv64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-riscv64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-riscv64@npm:0.25.8"
@@ -1751,6 +1856,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-s390x@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-s390x@npm:0.21.5"
+ conditions: os=linux & cpu=s390x
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-s390x@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-s390x@npm:0.25.8"
@@ -1772,6 +1884,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/linux-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/linux-x64@npm:0.21.5"
+ conditions: os=linux & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/linux-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/linux-x64@npm:0.25.8"
@@ -1800,6 +1919,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/netbsd-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/netbsd-x64@npm:0.21.5"
+ conditions: os=netbsd & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/netbsd-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/netbsd-x64@npm:0.25.8"
@@ -1828,6 +1954,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/openbsd-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/openbsd-x64@npm:0.21.5"
+ conditions: os=openbsd & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/openbsd-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/openbsd-x64@npm:0.25.8"
@@ -1856,6 +1989,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/sunos-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/sunos-x64@npm:0.21.5"
+ conditions: os=sunos & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/sunos-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/sunos-x64@npm:0.25.8"
@@ -1877,6 +2017,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/win32-arm64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/win32-arm64@npm:0.21.5"
+ conditions: os=win32 & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@esbuild/win32-arm64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/win32-arm64@npm:0.25.8"
@@ -1898,6 +2045,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/win32-ia32@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/win32-ia32@npm:0.21.5"
+ conditions: os=win32 & cpu=ia32
+ languageName: node
+ linkType: hard
+
"@esbuild/win32-ia32@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/win32-ia32@npm:0.25.8"
@@ -1919,6 +2073,13 @@ __metadata:
languageName: node
linkType: hard
+"@esbuild/win32-x64@npm:0.21.5":
+ version: 0.21.5
+ resolution: "@esbuild/win32-x64@npm:0.21.5"
+ conditions: os=win32 & cpu=x64
+ languageName: node
+ linkType: hard
+
"@esbuild/win32-x64@npm:0.25.8":
version: 0.25.8
resolution: "@esbuild/win32-x64@npm:0.25.8"
@@ -1944,7 +2105,7 @@ __metadata:
languageName: node
linkType: hard
-"@eslint/eslintrc@npm:^2.1.3":
+"@eslint/eslintrc@npm:^2.1.3, @eslint/eslintrc@npm:^2.1.4":
version: 2.1.4
resolution: "@eslint/eslintrc@npm:2.1.4"
dependencies:
@@ -1968,6 +2129,13 @@ __metadata:
languageName: node
linkType: hard
+"@eslint/js@npm:8.57.1":
+ version: 8.57.1
+ resolution: "@eslint/js@npm:8.57.1"
+ checksum: 10c0/b489c474a3b5b54381c62e82b3f7f65f4b8a5eaaed126546520bf2fede5532a8ed53212919fed1e9048dcf7f37167c8561d58d0ba4492a4244004e7793805223
+ languageName: node
+ linkType: hard
+
"@expo/cli@npm:0.24.20":
version: 0.24.20
resolution: "@expo/cli@npm:0.24.20"
@@ -2339,6 +2507,17 @@ __metadata:
languageName: node
linkType: hard
+"@humanwhocodes/config-array@npm:^0.13.0":
+ version: 0.13.0
+ resolution: "@humanwhocodes/config-array@npm:0.13.0"
+ dependencies:
+ "@humanwhocodes/object-schema": "npm:^2.0.3"
+ debug: "npm:^4.3.1"
+ minimatch: "npm:^3.0.5"
+ checksum: 10c0/205c99e756b759f92e1f44a3dc6292b37db199beacba8f26c2165d4051fe73a4ae52fdcfd08ffa93e7e5cb63da7c88648f0e84e197d154bbbbe137b2e0dd332e
+ languageName: node
+ linkType: hard
+
"@humanwhocodes/module-importer@npm:^1.0.1":
version: 1.0.1
resolution: "@humanwhocodes/module-importer@npm:1.0.1"
@@ -2346,7 +2525,7 @@ __metadata:
languageName: node
linkType: hard
-"@humanwhocodes/object-schema@npm:^2.0.2":
+"@humanwhocodes/object-schema@npm:^2.0.2, @humanwhocodes/object-schema@npm:^2.0.3":
version: 2.0.3
resolution: "@humanwhocodes/object-schema@npm:2.0.3"
checksum: 10c0/80520eabbfc2d32fe195a93557cef50dfe8c8905de447f022675aaf66abc33ae54098f5ea78548d925aa671cd4ab7c7daa5ad704fe42358c9b5e7db60f80696c
@@ -3530,8 +3709,11 @@ __metadata:
version: 0.0.0-use.local
resolution: "@phantom/swapper-sdk@workspace:packages/swapper-sdk"
dependencies:
+ "@solana/web3.js": "npm:^1.98.4"
"@types/jest": "npm:^29.5.12"
"@types/node": "npm:^20.11.0"
+ bs58: "npm:^6.0.0"
+ buffer: "npm:^6.0.3"
dotenv: "npm:^16.4.1"
eslint: "npm:8.53.0"
eventsource: "npm:^2.0.2"
@@ -4847,7 +5029,7 @@ __metadata:
languageName: node
linkType: hard
-"@solana/web3.js@npm:^1.87.6, @solana/web3.js@npm:^1.98.2":
+"@solana/web3.js@npm:^1.87.6, @solana/web3.js@npm:^1.98.2, @solana/web3.js@npm:^1.98.4":
version: 1.98.4
resolution: "@solana/web3.js@npm:1.98.4"
dependencies:
@@ -5156,6 +5338,22 @@ __metadata:
languageName: node
linkType: hard
+"@types/prop-types@npm:*":
+ version: 15.7.15
+ resolution: "@types/prop-types@npm:15.7.15"
+ checksum: 10c0/b59aad1ad19bf1733cf524fd4e618196c6c7690f48ee70a327eb450a42aab8e8a063fbe59ca0a5701aebe2d92d582292c0fb845ea57474f6a15f6994b0e260b2
+ languageName: node
+ linkType: hard
+
+"@types/react-dom@npm:^18.2.22":
+ version: 18.3.7
+ resolution: "@types/react-dom@npm:18.3.7"
+ peerDependencies:
+ "@types/react": ^18.0.0
+ checksum: 10c0/8bd309e2c3d1604a28a736a24f96cbadf6c05d5288cfef8883b74f4054c961b6b3a5e997fd5686e492be903c8f3380dba5ec017eff3906b1256529cd2d39603e
+ languageName: node
+ linkType: hard
+
"@types/react-dom@npm:^19.1.2":
version: 19.1.7
resolution: "@types/react-dom@npm:19.1.7"
@@ -5193,6 +5391,16 @@ __metadata:
languageName: node
linkType: hard
+"@types/react@npm:^18.2.66":
+ version: 18.3.23
+ resolution: "@types/react@npm:18.3.23"
+ dependencies:
+ "@types/prop-types": "npm:*"
+ csstype: "npm:^3.0.2"
+ checksum: 10c0/49331800b76572eb2992a5c44801dbf8c612a5f99c8f4e4200f06c7de6f3a6e9455c661784a6c5469df96fa45622cb4a9d0982c44e6a0d5719be5f2ef1f545ed
+ languageName: node
+ linkType: hard
+
"@types/react@npm:~19.0.10":
version: 19.0.14
resolution: "@types/react@npm:19.0.14"
@@ -5264,7 +5472,7 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/eslint-plugin@npm:^7.18.0":
+"@typescript-eslint/eslint-plugin@npm:^7.18.0, @typescript-eslint/eslint-plugin@npm:^7.2.0":
version: 7.18.0
resolution: "@typescript-eslint/eslint-plugin@npm:7.18.0"
dependencies:
@@ -5287,7 +5495,7 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/parser@npm:^7.18.0":
+"@typescript-eslint/parser@npm:^7.18.0, @typescript-eslint/parser@npm:^7.2.0":
version: 7.18.0
resolution: "@typescript-eslint/parser@npm:7.18.0"
dependencies:
@@ -5546,7 +5754,7 @@ __metadata:
languageName: node
linkType: hard
-"@vitejs/plugin-react@npm:^4.0.3, @vitejs/plugin-react@npm:^4.4.1":
+"@vitejs/plugin-react@npm:^4.0.3, @vitejs/plugin-react@npm:^4.2.1, @vitejs/plugin-react@npm:^4.4.1":
version: 4.7.0
resolution: "@vitejs/plugin-react@npm:4.7.0"
dependencies:
@@ -7918,6 +8126,86 @@ __metadata:
languageName: node
linkType: hard
+"esbuild@npm:^0.21.3":
+ version: 0.21.5
+ resolution: "esbuild@npm:0.21.5"
+ dependencies:
+ "@esbuild/aix-ppc64": "npm:0.21.5"
+ "@esbuild/android-arm": "npm:0.21.5"
+ "@esbuild/android-arm64": "npm:0.21.5"
+ "@esbuild/android-x64": "npm:0.21.5"
+ "@esbuild/darwin-arm64": "npm:0.21.5"
+ "@esbuild/darwin-x64": "npm:0.21.5"
+ "@esbuild/freebsd-arm64": "npm:0.21.5"
+ "@esbuild/freebsd-x64": "npm:0.21.5"
+ "@esbuild/linux-arm": "npm:0.21.5"
+ "@esbuild/linux-arm64": "npm:0.21.5"
+ "@esbuild/linux-ia32": "npm:0.21.5"
+ "@esbuild/linux-loong64": "npm:0.21.5"
+ "@esbuild/linux-mips64el": "npm:0.21.5"
+ "@esbuild/linux-ppc64": "npm:0.21.5"
+ "@esbuild/linux-riscv64": "npm:0.21.5"
+ "@esbuild/linux-s390x": "npm:0.21.5"
+ "@esbuild/linux-x64": "npm:0.21.5"
+ "@esbuild/netbsd-x64": "npm:0.21.5"
+ "@esbuild/openbsd-x64": "npm:0.21.5"
+ "@esbuild/sunos-x64": "npm:0.21.5"
+ "@esbuild/win32-arm64": "npm:0.21.5"
+ "@esbuild/win32-ia32": "npm:0.21.5"
+ "@esbuild/win32-x64": "npm:0.21.5"
+ dependenciesMeta:
+ "@esbuild/aix-ppc64":
+ optional: true
+ "@esbuild/android-arm":
+ optional: true
+ "@esbuild/android-arm64":
+ optional: true
+ "@esbuild/android-x64":
+ optional: true
+ "@esbuild/darwin-arm64":
+ optional: true
+ "@esbuild/darwin-x64":
+ optional: true
+ "@esbuild/freebsd-arm64":
+ optional: true
+ "@esbuild/freebsd-x64":
+ optional: true
+ "@esbuild/linux-arm":
+ optional: true
+ "@esbuild/linux-arm64":
+ optional: true
+ "@esbuild/linux-ia32":
+ optional: true
+ "@esbuild/linux-loong64":
+ optional: true
+ "@esbuild/linux-mips64el":
+ optional: true
+ "@esbuild/linux-ppc64":
+ optional: true
+ "@esbuild/linux-riscv64":
+ optional: true
+ "@esbuild/linux-s390x":
+ optional: true
+ "@esbuild/linux-x64":
+ optional: true
+ "@esbuild/netbsd-x64":
+ optional: true
+ "@esbuild/openbsd-x64":
+ optional: true
+ "@esbuild/sunos-x64":
+ optional: true
+ "@esbuild/win32-arm64":
+ optional: true
+ "@esbuild/win32-ia32":
+ optional: true
+ "@esbuild/win32-x64":
+ optional: true
+ bin:
+ esbuild: bin/esbuild
+ checksum: 10c0/fa08508adf683c3f399e8a014a6382a6b65542213431e26206c0720e536b31c09b50798747c2a105a4bbba1d9767b8d3615a74c2f7bf1ddf6d836cd11eb672de
+ languageName: node
+ linkType: hard
+
"esbuild@npm:^0.25.0, esbuild@npm:~0.25.0":
version: 0.25.8
resolution: "esbuild@npm:0.25.8"
@@ -8200,6 +8488,15 @@ __metadata:
languageName: node
linkType: hard
+"eslint-plugin-react-refresh@npm:^0.4.6":
+ version: 0.4.20
+ resolution: "eslint-plugin-react-refresh@npm:0.4.20"
+ peerDependencies:
+ eslint: ">=8.40"
+ checksum: 10c0/2ccf4ba28f1dcbcb9e773e46eae1e61e568bba69281a700eb26fd762152e4e90a78c991f9c8173342a7cd2a82f3f52fedb40a1e81360cef9c40ea5b814fa3613
+ languageName: node
+ linkType: hard
+
"eslint-plugin-react@npm:^7.33.0":
version: 7.37.5
resolution: "eslint-plugin-react@npm:7.37.5"
@@ -8302,6 +8599,54 @@ __metadata:
languageName: node
linkType: hard
+"eslint@npm:^8.57.0":
+ version: 8.57.1
+ resolution: "eslint@npm:8.57.1"
+ dependencies:
+ "@eslint-community/eslint-utils": "npm:^4.2.0"
+ "@eslint-community/regexpp": "npm:^4.6.1"
+ "@eslint/eslintrc": "npm:^2.1.4"
+ "@eslint/js": "npm:8.57.1"
+ "@humanwhocodes/config-array": "npm:^0.13.0"
+ "@humanwhocodes/module-importer": "npm:^1.0.1"
+ "@nodelib/fs.walk": "npm:^1.2.8"
+ "@ungap/structured-clone": "npm:^1.2.0"
+ ajv: "npm:^6.12.4"
+ chalk: "npm:^4.0.0"
+ cross-spawn: "npm:^7.0.2"
+ debug: "npm:^4.3.2"
+ doctrine: "npm:^3.0.0"
+ escape-string-regexp: "npm:^4.0.0"
+ eslint-scope: "npm:^7.2.2"
+ eslint-visitor-keys: "npm:^3.4.3"
+ espree: "npm:^9.6.1"
+ esquery: "npm:^1.4.2"
+ esutils: "npm:^2.0.2"
+ fast-deep-equal: "npm:^3.1.3"
+ file-entry-cache: "npm:^6.0.1"
+ find-up: "npm:^5.0.0"
+ glob-parent: "npm:^6.0.2"
+ globals: "npm:^13.19.0"
+ graphemer: "npm:^1.4.0"
+ ignore: "npm:^5.2.0"
+ imurmurhash: "npm:^0.1.4"
+ is-glob: "npm:^4.0.0"
+ is-path-inside: "npm:^3.0.3"
+ js-yaml: "npm:^4.1.0"
+ json-stable-stringify-without-jsonify: "npm:^1.0.1"
+ levn: "npm:^0.4.1"
+ lodash.merge: "npm:^4.6.2"
+ minimatch: "npm:^3.1.2"
+ natural-compare: "npm:^1.4.0"
+ optionator: "npm:^0.9.3"
+ strip-ansi: "npm:^6.0.1"
+ text-table: "npm:^0.2.0"
+ bin:
+ eslint: bin/eslint.js
+ checksum: 10c0/1fd31533086c1b72f86770a4d9d7058ee8b4643fd1cfd10c7aac1ecb8725698e88352a87805cf4b2ce890aa35947df4b4da9655fb7fdfa60dbb448a43f6ebcf1
+ languageName: node
+ linkType: hard
+
"espree@npm:^9.6.0, espree@npm:^9.6.1":
version: 9.6.1
resolution: "espree@npm:9.6.1"
@@ -11681,7 +12026,7 @@ __metadata:
languageName: node
linkType: hard
-"loose-envify@npm:^1.0.0, loose-envify@npm:^1.4.0":
+"loose-envify@npm:^1.0.0, loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0":
version: 1.4.0
resolution: "loose-envify@npm:1.4.0"
dependencies:
@@ -13004,7 +13349,7 @@ __metadata:
languageName: node
linkType: hard
-"postcss@npm:^8.5.3":
+"postcss@npm:^8.4.43, postcss@npm:^8.5.3":
version: 8.5.6
resolution: "postcss@npm:8.5.6"
dependencies:
@@ -13325,6 +13670,18 @@ __metadata:
languageName: node
linkType: hard
+"react-dom@npm:^18.2.0":
+ version: 18.3.1
+ resolution: "react-dom@npm:18.3.1"
+ dependencies:
+ loose-envify: "npm:^1.1.0"
+ scheduler: "npm:^0.23.2"
+ peerDependencies:
+ react: ^18.3.1
+ checksum: 10c0/a752496c1941f958f2e8ac56239172296fcddce1365ce45222d04a1947e0cc5547df3e8447f855a81d6d39f008d7c32eab43db3712077f09e3f67c4874973e85
+ languageName: node
+ linkType: hard
+
"react-fast-compare@npm:^3.2.2":
version: 3.2.2
resolution: "react-fast-compare@npm:3.2.2"
@@ -13577,6 +13934,15 @@ __metadata:
languageName: node
linkType: hard
+"react@npm:^18.2.0":
+ version: 18.3.1
+ resolution: "react@npm:18.3.1"
+ dependencies:
+ loose-envify: "npm:^1.1.0"
+ checksum: 10c0/283e8c5efcf37802c9d1ce767f302dd569dd97a70d9bb8c7be79a789b9902451e0d16334b05d73299b20f048cbc3c7d288bbbde10b701fa194e2089c237dbea3
+ languageName: node
+ linkType: hard
+
"read-yaml-file@npm:^1.1.0":
version: 1.1.0
resolution: "read-yaml-file@npm:1.1.0"
@@ -13946,7 +14312,7 @@ __metadata:
languageName: node
linkType: hard
-"rollup@npm:^4.34.9":
+"rollup@npm:^4.20.0, rollup@npm:^4.34.9":
version: 4.46.2
resolution: "rollup@npm:4.46.2"
dependencies:
@@ -14148,6 +14514,15 @@ __metadata:
languageName: node
linkType: hard
+"scheduler@npm:^0.23.2":
+ version: 0.23.2
+ resolution: "scheduler@npm:0.23.2"
+ dependencies:
+ loose-envify: "npm:^1.1.0"
+ checksum: 10c0/26383305e249651d4c58e6705d5f8425f153211aef95f15161c151f7b8de885f24751b377e4a0b3dd42cce09aad3f87a61dab7636859c0d89b7daf1a1e2a5c78
+ languageName: node
+ linkType: hard
+
"scheduler@npm:^0.26.0":
version: 0.26.0
resolution: "scheduler@npm:0.26.0"
@@ -14910,6 +15285,30 @@ __metadata:
languageName: node
linkType: hard
+"swapper-demo@workspace:examples/swapper-demo":
+ version: 0.0.0-use.local
+ resolution: "swapper-demo@workspace:examples/swapper-demo"
+ dependencies:
+ "@phantom/react-sdk": "workspace:^"
+ "@phantom/swapper-sdk": "workspace:^"
+ "@solana/web3.js": "npm:^1.98.4"
+ "@types/react": "npm:^18.2.66"
+ "@types/react-dom": "npm:^18.2.22"
+ "@typescript-eslint/eslint-plugin": "npm:^7.2.0"
+ "@typescript-eslint/parser": "npm:^7.2.0"
+ "@vitejs/plugin-react": "npm:^4.2.1"
+ bs58: "npm:^6.0.0"
+ buffer: "npm:^6.0.3"
+ eslint: "npm:^8.57.0"
+ eslint-plugin-react-hooks: "npm:^4.6.0"
+ eslint-plugin-react-refresh: "npm:^0.4.6"
+ react: "npm:^18.2.0"
+ react-dom: "npm:^18.2.0"
+ typescript: "npm:^5.2.2"
+ vite: "npm:^5.2.0"
+ languageName: unknown
+ linkType: soft
+
"symbol-tree@npm:^3.2.4":
version: 3.2.4
resolution: "symbol-tree@npm:3.2.4"
@@ -15874,6 +16273,49 @@ __metadata:
languageName: node
linkType: hard
+"vite@npm:^5.2.0":
+ version: 5.4.19
+ resolution: "vite@npm:5.4.19"
+ dependencies:
+ esbuild: "npm:^0.21.3"
+ fsevents: "npm:~2.3.3"
+ postcss: "npm:^8.4.43"
+ rollup: "npm:^4.20.0"
+ peerDependencies:
+ "@types/node": ^18.0.0 || >=20.0.0
+ less: "*"
+ lightningcss: ^1.21.0
+ sass: "*"
+ sass-embedded: "*"
+ stylus: "*"
+ sugarss: "*"
+ terser: ^5.4.0
+ dependenciesMeta:
+ fsevents:
+ optional: true
+ peerDependenciesMeta:
+ "@types/node":
+ optional: true
+ less:
+ optional: true
+ lightningcss:
+ optional: true
+ sass:
+ optional: true
+ sass-embedded:
+ optional: true
+ stylus:
+ optional: true
+ sugarss:
+ optional: true
+ terser:
+ optional: true
+ bin:
+ vite: bin/vite.js
+ checksum: 10c0/c97601234dba482cea5290f2a2ea0fcd65e1fab3df06718ea48adc8ceb14bc3129508216c4989329c618f6a0470b42f439677a207aef62b0c76f445091c2d89e
+ languageName: node
+ linkType: hard
+
"vite@npm:^6.3.5":
version: 6.3.5
resolution: "vite@npm:6.3.5"