Skip to content

Latest commit

Β 

History

History
423 lines (344 loc) Β· 13.1 KB

File metadata and controls

423 lines (344 loc) Β· 13.1 KB

CI codecov

Live Demo

Swapper - Sponsored SVM to EVM Bridge

A bridge-agnostic, fully off-chain cross-chain swap system that supports bridging tokens from Solana (SVM) to EVM chains using Relay and DeBridge providers.

Features

  • Bridge-Agnostic Design - Easily extensible to support additional providers
  • Sponsored Transactions - All Solana-side costs are sponsored
  • Economic Guarantees - Sponsor never has net loss
  • SPL & Token-2022 Support - Handles transfer fees, dust accounts, and uncloseable accounts
  • Multi-Provider Quotes - Aggregates quotes from Relay and DeBridge
  • Fee Optimization - Users pay USDC (preferred) or SOL fees
  • Swap History - Track all swaps via Prisma database
  • Production-Ready UI - Built with Next.js 16, React 19, TypeScript, and StyleX

Architecture Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         Frontend (Next.js)              β”‚
β”‚   - SwapWidget                          β”‚
β”‚   - Wallet Connection                   β”‚
β”‚   - StyleX UI Components                β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
                  ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚        API Routes (Next.js)             β”‚
β”‚   - /api/quote                          β”‚
β”‚   - /api/execute                        β”‚
β”‚   - /api/execute/confirm                β”‚
β”‚   - /api/status/[id]                    β”‚
β”‚   - /api/history                        β”‚
β”‚   - /api/balances                       β”‚
β”‚   - /api/health                         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
                  ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         Service Layer                   β”‚
β”‚   - SwapOrchestrator                    β”‚
β”‚   - FeeCalculator & Validator           β”‚
β”‚   - SolanaService (SPL/Token-2022)      β”‚
β”‚   - TransactionBuilder                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
                  ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚    Bridge Provider Abstraction          β”‚
β”‚   - IBridgeProvider Interface           β”‚
β”‚   - RelayProvider                       β”‚
β”‚   - DeBridgeProvider                    β”‚
β”‚   - ProviderRegistry                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚
                  ↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   External Services & Storage           β”‚
β”‚   - Relay API                           β”‚
β”‚   - DeBridge API                        β”‚
β”‚   - Pyth on-chain oracle (SOL/USD)      β”‚
β”‚   - Prisma DB (PostgreSQL)              β”‚
β”‚   - Solana RPC                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Setup Instructions

Prerequisites

  • Node.js 18+ or Bun
  • Docker & Docker Compose (for PostgreSQL)
  • Solana wallet with funds (for sponsor)

1. Install Dependencies

bun install

2. Start PostgreSQL via Docker Compose

docker compose up -d

This starts a PostgreSQL 17 instance with the default credentials configured in .env.example. The data is persisted in a Docker volume (pgdata).

To stop the database:

docker compose down        # stop containers (data preserved)
docker compose down -v     # stop and delete volume (data lost)

3. Environment Configuration

Create a .env file based on .env.example:

cp .env.example .env

Edit .env with your configuration.

Required Variables

Variable Description
DATABASE_URL PostgreSQL connection string
SPONSOR_WALLET_PRIVATE_KEY Base58-encoded Solana keypair for sponsoring transactions

Optional Variables (defaults provided)

Variable Default Description
SOLANA_RPC_URL https://api.mainnet-beta.solana.com Solana RPC endpoint
SOLANA_RPC_WEBSOCKET_URL - Solana WebSocket endpoint
RELAY_API_URL https://api.relay.link Relay bridge API base URL
RELAY_API_KEY - Optional Relay API key for higher rate limits
DEBRIDGE_API_URL https://dln.debridge.finance DeBridge DLN API base URL
FEE_VOLATILITY_BUFFER 0.15 Price volatility buffer (15%)
MAX_QUOTE_DRIFT 0.02 Maximum acceptable quote drift (2%)
QUOTE_EXPIRY_SECONDS 30 Quote validity period in seconds
LOG_LEVEL info Log level: debug, info, warn, error
NODE_ENV development Node environment

Docker Compose Variables

Variable Default Description
POSTGRES_USER swapper PostgreSQL username
POSTGRES_PASSWORD swapper PostgreSQL password
POSTGRES_DB swapper PostgreSQL database name

4. Database Setup

Generate Prisma client and push schema:

bun db:generate
bun db:push

5. Run Development Server

bun dev

Visit http://localhost:3000

How It Works

User Flow

  1. Connect Wallet - User connects Solana wallet (Phantom, etc.)

  2. Input Swap Details

    • Select source token (SPL/Token-2022)
    • Enter amount
    • Select destination chain (Ethereum, Polygon, etc.)
    • Enter destination token address
    • Enter destination wallet address
  3. Get Quote

    • Quotes auto-fetch when all inputs are valid (500ms debounce)
    • System fetches quotes from all providers (Relay, DeBridge)
    • Calculates fees (sponsor costs + buffer)
    • Displays best quote with provider info
  4. Execute Swap

    • System builds sponsored transaction
    • Fee transfer instruction added first (critical!)
    • User signs transaction
    • Sponsor co-signs and submits
    • System monitors status
  5. Monitor Progress

    • Real-time status updates
    • Source and destination transaction links
    • Completion notification

Economic Guarantees

Critical: The fee transfer MUST be the first instruction in every transaction.

  1. User Fee Calculation:

    totalFee = (sponsorCosts + volatilityBuffer + platformFee)
    
    where:
    - sponsorCosts = gas + priority fees + rent
    - volatilityBuffer = 15% (configurable)
    - platformFee = 0% (configurable)
    
  2. Pre-Execution Validation:

    • Quote hasn't expired
    • Price drift within acceptable range (2%)
    • User fee covers sponsor costs
    • User has sufficient balance
  3. Transaction Structure:

    1. User pays fee to sponsor (FIRST!)
    2. Bridge swap instructions
    
    If ANY instruction fails, entire transaction reverts
    Ensures sponsor never pays without receiving fee
    

API Endpoints

All error responses follow a consistent format:

{
  "success": false,
  "error": "Human-readable error message",
  "errorCode": "MACHINE_READABLE_CODE"
}

Error codes: VALIDATION_ERROR, NOT_FOUND, QUOTE_ERROR, INSUFFICIENT_BALANCE, FEE_ERROR, BRIDGE_ERROR, TRANSACTION_ERROR, CONFIG_ERROR, PRICE_ORACLE_ERROR, INTERNAL_ERROR.

POST /api/quote

Get quotes from all providers.

Request:

{
  "sourceToken": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
  "sourceAmount": "100",
  "destChain": "1",
  "destToken": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
  "userWallet": "...",
  "destWallet": "0x..."
}

Response:

{
  "success": true,
  "quotes": [...],
  "bestQuote": {...},
  "recommendedQuote": {...}
}

POST /api/execute

Prepare swap transaction.

Request:

{
  "quote": {...},
  "userWallet": "...",
  "feeToken": "USDC"
}

Response:

{
  "success": true,
  "swapId": "...",
  "transaction": "base64_encoded_tx",
  "userFee": {...},
  "sponsorCosts": {...},
  "validUntil": "2024-..."
}

POST /api/execute/confirm

Execute signed transaction.

Request:

{
  "swapId": "...",
  "signedTransaction": "base64_encoded_signed_tx"
}

Response:

{
  "success": true,
  "swapId": "...",
  "status": "submitted",
  "signature": "..."
}

GET /api/status/[id]

Get swap status.

Response:

{
  "success": true,
  "status": {
    "swapId": "...",
    "status": "completed",
    "sourceChainTx": "...",
    "destChainTx": "...",
    "progress": 100
  }
}

GET /api/balances?wallet=...

Get wallet token balances.

GET /api/history?wallet=...&limit=50

Get swap history and statistics.

GET /api/health

Health check.

Adding a New Bridge Provider

  1. Create a new provider class implementing IBridgeProvider:
export class NewBridgeProvider implements IBridgeProvider {
  name = 'newbridge';

  async supportsRoute(params): Promise<boolean> { ... }
  async getQuote(params: QuoteParams): Promise<BridgeQuote> { ... }
  async validateQuote(quote: BridgeQuote): Promise<QuoteValidation> { ... }
  async buildTransaction(quote, sponsor, userWallet): Promise<Uint8Array> { ... }
  async getStatus(orderId: string): Promise<ExecutionStatus> { ... }
  async estimateCosts(params: QuoteParams): Promise<CostBreakdown> { ... }
}
  1. Register in lib/swap/SwapOrchestrator.ts (getSwapOrchestrator()):
providerRegistry.register(new NewBridgeProvider());

The provider will automatically be included in quote aggregation.

Scripts

# Development
bun dev              # Start dev server
bun build            # Build for production
bun start            # Start production server

# Database
bun db:generate      # Generate Prisma client
bun db:push          # Push schema to database
bun db:studio        # Open Prisma Studio

# Testing
bun test             # Run unit tests
bun test:watch       # Run tests in watch mode
bun test:e2e         # Run E2E tests (requires running dev server)

# Docker
docker compose up -d   # Start PostgreSQL
docker compose down    # Stop PostgreSQL

Tech Stack

  • Framework: Next.js 16 (App Router)
  • Language: TypeScript
  • UI: React 19 + StyleX (Meta)
  • Database: PostgreSQL + Prisma 7
  • Blockchain: @solana/kit v6, @solana/react, SPL Token, Token-2022
  • Wallet: @wallet-standard/react
  • Price Oracle: Pyth on-chain oracle (SOL/USD)
  • Validation: Zod 4 + @t3-oss/env-nextjs
  • Testing: Jest (unit) + Playwright (E2E)
  • Package Manager: Bun

Project Structure

swapper/
β”œβ”€β”€ app/                    # Next.js app directory
β”‚   β”œβ”€β”€ api/               # API routes (quote, execute, status, etc.)
β”‚   β”œβ”€β”€ actions/           # Server actions
β”‚   β”œβ”€β”€ history/           # History page
β”‚   β”œβ”€β”€ layout.tsx         # Root layout (wallet provider + nav)
β”‚   └── page.tsx           # Home page (swap widget)
β”œβ”€β”€ lib/                    # Core business logic
β”‚   β”œβ”€β”€ bridges/           # Bridge provider abstraction
β”‚   β”œβ”€β”€ solana/            # Solana RPC service (SPL + Token-2022)
β”‚   β”œβ”€β”€ fees/              # Fee calculation & validation
β”‚   β”œβ”€β”€ swap/              # Swap orchestrator
β”‚   β”œβ”€β”€ transactions/      # Transaction builder
β”‚   β”œβ”€β”€ config/            # Configuration, constants, env
β”‚   β”œβ”€β”€ db/                # Prisma client & repositories
β”‚   β”œβ”€β”€ middleware/         # Rate limiter
β”‚   β”œβ”€β”€ utils/             # Logger, retry, formatting
β”‚   └── errors.ts          # Error type hierarchy
β”œβ”€β”€ hooks/                  # React hooks (useSwap, useAutoQuote, etc.)
β”œβ”€β”€ components/             # React components
β”‚   β”œβ”€β”€ swap/              # Swap widget & token panels
β”‚   β”œβ”€β”€ wallet/            # Wallet provider & connect button
β”‚   β”œβ”€β”€ history/           # Swap history list
β”‚   β”œβ”€β”€ layout/            # Nav header
β”‚   └── ui/                # Reusable UI components
β”œβ”€β”€ types/                  # TypeScript type definitions
β”œβ”€β”€ styles/                 # StyleX tokens & themes
β”œβ”€β”€ prisma/                 # Database schema
β”œβ”€β”€ proxy.ts               # Next.js proxy (rate limiting, security headers)
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ unit/              # Unit tests (Jest)
β”‚   └── e2e/               # E2E tests (Playwright)
└── docker-compose.yml      # PostgreSQL setup