diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index b5acf7c..016ee93 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -68,6 +68,3 @@ jobs:
working-directory: packages/actions
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
-
-
-
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 921bc01..f5bfb8b 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -28,7 +28,7 @@ Thank you for your interest in contributing to Pipeit! This document provides gu
This is a monorepo managed with Turbo and pnpm workspaces:
- `packages/core/` - Main transaction builder with execution strategies, Flow API, and Kit integration
-- `packages/actions/` - High-level DeFi actions with pluggable protocol adapters
+- `packages/actions/` - InstructionPlan factories for DeFi (Titan, Metis)
- `packages/fastlane/` - Native Rust QUIC client for direct TPU submission (NAPI)
- `examples/next-js/` - Next.js example application demonstrating usage
@@ -103,9 +103,9 @@ pnpm test
### @pipeit/actions
-- Follow adapter pattern for protocol integrations
-- Ensure adapters are pluggable and testable
-- Document adapter interfaces and requirements
+- Build Kit-compatible InstructionPlans
+- Follow existing Titan/Metis patterns for new integrations
+- Document quote/route/plan building pipeline
- Consider API rate limits and error handling
### @pipeit/fastlane
diff --git a/README.md b/README.md
index f4b32d5..a16448a 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,7 @@ Pipeit is a comprehensive TypeScript SDK for building and executing Solana trans
Built on modern Solana libraries (@solana/kit) with a focus on type safety, developer experience, and production readiness.
**Key Features:**
+
- Type-safe transaction building with compile-time validation
- Multiple execution strategies (Standard RPC, Jito Bundles, Parallel Execution, TPU direct)
- Multi-step flows with dynamic context between steps
@@ -19,31 +20,37 @@ Built on modern Solana libraries (@solana/kit) with a focus on type safety, deve
## Packages
-| Package | Description | Docs |
-|---------|-------------|------|
-| [@pipeit/core](./packages/core) | Transaction builder with smart defaults, flows, and execution strategies | [README](./packages/core/README.md) |
-| [@pipeit/actions](./packages/actions) | High-level DeFi actions with pluggable adapters | [README](./packages/actions/README.md) |
-| [@pipeit/fastlane](./packages/fastlane) | Native Rust QUIC client for direct TPU submission | [Package](./packages/fastlane) |
+| Package | Description | Docs |
+| --------------------------------------- | ------------------------------------------------------------------------ | -------------------------------------- |
+| [@pipeit/core](./packages/core) | Transaction builder with smart defaults, flows, and execution strategies | [README](./packages/core/README.md) |
+| [@pipeit/actions](./packages/actions) | InstructionPlan factories for DeFi (Titan, Metis) | [README](./packages/actions/README.md) |
+| [@pipeit/fastlane](./packages/fastlane) | Native Rust QUIC client for direct TPU submission | [Package](./packages/fastlane) |
## Package Overview
### @pipeit/core
+
The foundation package for transaction building:
+
- TransactionBuilder with auto-blockhash, auto-retry, and priority fees
- Flow API for multi-step workflows with dynamic context
- Multiple execution strategies (RPC, Jito bundles, parallel execution, TPU direct)
- Kit instruction-plans integration
- Server exports for server components based TPU handlers
-### @pipeit/actions (WIP)
-High-level DeFi operations:
-- Simple, composable API for swaps and other DeFi actions
-- Pluggable adapters (Jupiter, with more coming)
-- Automatic address lookup table handling
-- Lifecycle hooks for monitoring execution
+### @pipeit/actions
+
+Composable InstructionPlan factories for DeFi:
+
+- Kit-compatible InstructionPlans for swap operations
+- Titan and Metis aggregator integration
+- Address lookup table support
+- Composable with Kit's plan combinators
### @pipeit/fastlane
+
Ultra-fast transaction submission:
+
- Native Rust QUIC implementation via NAPI
- Direct TPU submission bypassing RPC nodes
- Continuous resubmission until confirmation
@@ -57,13 +64,14 @@ Ultra-fast transaction submission:
pipeit/
├── packages/
│ ├── @pipeit/core # Transaction builder, flows, execution
-│ ├── @pipeit/actions # High-level DeFi actions
+│ ├── @pipeit/actions # InstructionPlan factories for DeFi
│ └── @pipeit/fastlane # Native QUIC TPU client
└── examples/
└── next-js/ # Example application
```
**Choosing a Package:**
+
- Building transactions? → `@pipeit/core`
- DeFi operations (swaps)? → `@pipeit/actions` + `@pipeit/core`
- Ultra-fast submission? → `@pipeit/fastlane` + `@pipeit/core`
@@ -74,7 +82,7 @@ pipeit/
# Transaction builder (recommended starting point)
pnpm install @pipeit/core @solana/kit
-# High-level DeFi actions
+# DeFi operations (swaps via Titan/Metis)
pnpm install @pipeit/actions @pipeit/core @solana/kit
# TPU direct submission (server-side only)
@@ -126,43 +134,47 @@ const result = await createFlow({ rpc, rpcSubscriptions, signer })
### DeFi Swap
```typescript
-import { pipe } from '@pipeit/actions';
-import { jupiter } from '@pipeit/actions/adapters';
+import { getTitanSwapPlan } from '@pipeit/actions/titan';
+import { executePlan } from '@pipeit/core';
-const result = await pipe({
- rpc,
- rpcSubscriptions,
- signer,
- adapters: { swap: jupiter() },
-})
- .swap({
+// Get a swap plan from Titan
+const { plan, lookupTableAddresses, quote } = await getTitanSwapPlan({
+ swap: {
inputMint: 'So11111111111111111111111111111111111111112', // SOL
outputMint: 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v', // USDC
amount: 100_000_000n, // 0.1 SOL
slippageBps: 50,
- })
- .execute();
+ },
+ transaction: {
+ userPublicKey: signer.address,
+ },
+});
+
+// Execute with ALT support
+await executePlan(plan, {
+ rpc,
+ rpcSubscriptions,
+ signer,
+ lookupTableAddresses,
+});
```
## Execution Strategies
Pipeit supports multiple execution strategies for different use cases:
-| Preset | Description | Use Case |
-|--------|-------------|----------|
-| `'standard'` | Default RPC submission | General transactions |
-| `'economical'` | Jito bundle only | MEV-sensitive swaps |
-| `'fast'` | Jito + parallel RPC race | Time-sensitive operations |
-| `'ultra'` | TPU direct + Jito race | Fastest possible (requires `@pipeit/fastlane`) |
+| Preset | Description | Use Case |
+| -------------- | ------------------------ | ---------------------------------------------- |
+| `'standard'` | Default RPC submission | General transactions |
+| `'economical'` | Jito bundle only | MEV-sensitive swaps |
+| `'fast'` | Jito + parallel RPC race | Time-sensitive operations |
+| `'ultra'` | TPU direct + Jito race | Fastest possible (requires `@pipeit/fastlane`) |
```typescript
-const signature = await new TransactionBuilder({ rpc })
- .setFeePayerSigner(signer)
- .addInstruction(instruction)
- .execute({
- rpcSubscriptions,
- execution: 'fast', // or 'standard', 'economical', 'ultra'
- });
+const signature = await new TransactionBuilder({ rpc }).setFeePayerSigner(signer).addInstruction(instruction).execute({
+ rpcSubscriptions,
+ execution: 'fast', // or 'standard', 'economical', 'ultra'
+});
```
For custom configuration, see the [@pipeit/core README](./packages/core/README.md).
@@ -225,11 +237,13 @@ export { tpuHandler as POST } from '@pipeit/core/server';
## Development
### Prerequisites
+
- Node.js 20+
- pnpm 10+
- Rust (for @pipeit/fastlane development)
### Setup
+
```bash
git clone https://github.com/stevesarmiento/pipeit.git
cd pipeit
@@ -237,6 +251,7 @@ pnpm install
```
### Commands
+
```bash
pnpm build # Build all packages
pnpm test # Run all tests
@@ -250,5 +265,5 @@ Contributions are welcome. Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for
## License
-MIT
+MIT
[LICENSE.md](.LICENSE.md)
diff --git a/examples/next-js/README.md b/examples/next-js/README.md
index 1e23a62..ebc261c 100644
--- a/examples/next-js/README.md
+++ b/examples/next-js/README.md
@@ -31,15 +31,15 @@ Demonstrates the value proposition of Pipeit with:
Interactive demos of various pipeline patterns with real mainnet transactions:
-| Example | Description |
-|---------|-------------|
-| **Simple Transfer** | Single instruction, single transaction - baseline example |
-| **Batched Transfers** | Multiple transfers batched into one atomic transaction |
-| **Mixed Pipeline** | Instruction and transaction steps - shows when batching breaks |
-| **Jupiter Swap** | Token swap using Jupiter aggregator |
-| **Pipe Multi-Swap** | SOL → USDC → BONK sequential swaps with Flow orchestration |
-| **Jito Bundle** | MEV-protected bundle submission with Jito tip instructions |
-| **TPU Direct** | Direct QUIC submission to validator TPU - bypass RPC for max speed |
+| Example | Description |
+| --------------------- | ------------------------------------------------------------------ |
+| **Simple Transfer** | Single instruction, single transaction - baseline example |
+| **Batched Transfers** | Multiple transfers batched into one atomic transaction |
+| **Mixed Pipeline** | Instruction and transaction steps - shows when batching breaks |
+| **Jupiter Swap** | Token swap using Jupiter aggregator |
+| **Titan Swap** | Token swap using Titan aggregator |
+| **Jito Bundle** | MEV-protected bundle submission with Jito tip instructions |
+| **TPU Direct** | Direct QUIC submission to validator TPU - bypass RPC for max speed |
Each example includes:
@@ -102,7 +102,7 @@ Multi-step examples demonstrate:
## Dependencies
- `@pipeit/core` - Transaction builder
-- `@pipeit/actions` - DeFi actions (Jupiter swaps)
+- `@pipeit/actions` - DeFi actions (Titan, Metis swaps)
- `@pipeit/fastlane` - TPU direct submission
- `@solana/kit` - Solana primitives
- `@solana/connector` - Wallet connection
diff --git a/examples/next-js/app/api/jupiter/quote/route.ts b/examples/next-js/app/api/jupiter/quote/route.ts
index d681d92..f0ed37e 100644
--- a/examples/next-js/app/api/jupiter/quote/route.ts
+++ b/examples/next-js/app/api/jupiter/quote/route.ts
@@ -1,30 +1,79 @@
import { NextRequest, NextResponse } from 'next/server';
+const JUPITER_API_BASE = 'https://api.jup.ag/swap/v1';
+
/**
- * Next.js API route proxy for Jupiter quote API.
- * Proxies requests to Jupiter's quote API to work around network/DNS restrictions.
+ * Next.js API route proxy for Jupiter Metis quote API.
+ * Proxies requests to Jupiter's quote API with API key authentication.
*/
export async function GET(request: NextRequest) {
const searchParams = request.nextUrl.searchParams;
- const params = new URLSearchParams({
- inputMint: searchParams.get('inputMint') || '',
- outputMint: searchParams.get('outputMint') || '',
- amount: searchParams.get('amount') || '',
- slippageBps: searchParams.get('slippageBps') || '50',
- onlyDirectRoutes: searchParams.get('onlyDirectRoutes') || 'false',
- asLegacyTransaction: searchParams.get('asLegacyTransaction') || 'false',
- });
+ const params = new URLSearchParams();
+
+ // Required params
+ const inputMint = searchParams.get('inputMint');
+ const outputMint = searchParams.get('outputMint');
+ const amount = searchParams.get('amount');
+
+ if (!inputMint || !outputMint || !amount) {
+ return NextResponse.json(
+ { error: 'Missing required parameters: inputMint, outputMint, amount' },
+ { status: 400 },
+ );
+ }
+
+ params.set('inputMint', inputMint);
+ params.set('outputMint', outputMint);
+ params.set('amount', amount);
+
+ // Optional params
+ const slippageBps = searchParams.get('slippageBps');
+ if (slippageBps) params.set('slippageBps', slippageBps);
+
+ const swapMode = searchParams.get('swapMode');
+ if (swapMode) params.set('swapMode', swapMode);
+
+ const dexes = searchParams.get('dexes');
+ if (dexes) params.set('dexes', dexes);
+
+ const excludeDexes = searchParams.get('excludeDexes');
+ if (excludeDexes) params.set('excludeDexes', excludeDexes);
+
+ const restrictIntermediateTokens = searchParams.get('restrictIntermediateTokens');
+ if (restrictIntermediateTokens) params.set('restrictIntermediateTokens', restrictIntermediateTokens);
+
+ const onlyDirectRoutes = searchParams.get('onlyDirectRoutes');
+ if (onlyDirectRoutes) params.set('onlyDirectRoutes', onlyDirectRoutes);
+
+ const asLegacyTransaction = searchParams.get('asLegacyTransaction');
+ if (asLegacyTransaction) params.set('asLegacyTransaction', asLegacyTransaction);
+
+ const platformFeeBps = searchParams.get('platformFeeBps');
+ if (platformFeeBps) params.set('platformFeeBps', platformFeeBps);
+
+ const maxAccounts = searchParams.get('maxAccounts');
+ if (maxAccounts) params.set('maxAccounts', maxAccounts);
try {
- const response = await fetch(`https://lite-api.jup.ag/swap/v1/quote?${params}`, {
- headers: {
- Accept: 'application/json',
- },
+ const headers: Record = {
+ Accept: 'application/json',
+ };
+
+ // Add API key if available
+ if (process.env.JUPITER_API_KEY) {
+ headers['x-api-key'] = process.env.JUPITER_API_KEY;
+ }
+
+ const response = await fetch(`${JUPITER_API_BASE}/quote?${params}`, {
+ headers,
+ cache: 'no-store',
});
if (!response.ok) {
- throw new Error(`Jupiter API error: ${response.status}`);
+ const errorText = await response.text();
+ console.error('Jupiter quote API error:', response.status, errorText);
+ throw new Error(`Jupiter API error: ${response.status} - ${errorText.substring(0, 200)}`);
}
const data = await response.json();
diff --git a/examples/next-js/app/api/jupiter/swap-instructions/route.ts b/examples/next-js/app/api/jupiter/swap-instructions/route.ts
index 64f151a..1a1b079 100644
--- a/examples/next-js/app/api/jupiter/swap-instructions/route.ts
+++ b/examples/next-js/app/api/jupiter/swap-instructions/route.ts
@@ -1,8 +1,10 @@
import { NextRequest, NextResponse } from 'next/server';
+const JUPITER_API_BASE = 'https://api.jup.ag/swap/v1';
+
/**
- * Next.js API route proxy for Jupiter swap-instructions API.
- * Proxies requests to Jupiter's swap-instructions API to work around network/DNS restrictions.
+ * Next.js API route proxy for Jupiter Metis swap-instructions API.
+ * Proxies requests to Jupiter's swap-instructions API with API key authentication.
*/
export async function POST(request: NextRequest) {
try {
@@ -17,18 +19,26 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: 'Missing required field: userPublicKey' }, { status: 400 });
}
- const response = await fetch('https://lite-api.jup.ag/swap/v1/swap-instructions', {
+ const headers: Record = {
+ 'Content-Type': 'application/json',
+ Accept: 'application/json',
+ };
+
+ // Add API key if available
+ if (process.env.JUPITER_API_KEY) {
+ headers['x-api-key'] = process.env.JUPITER_API_KEY;
+ }
+
+ const response = await fetch(`${JUPITER_API_BASE}/swap-instructions`, {
method: 'POST',
- headers: {
- 'Content-Type': 'application/json',
- Accept: 'application/json',
- },
+ headers,
+ cache: 'no-store',
body: JSON.stringify(body),
});
if (!response.ok) {
const errorText = await response.text();
- console.error('Jupiter API error response:', response.status, errorText);
+ console.error('Jupiter swap-instructions API error:', response.status, errorText);
throw new Error(`Jupiter API error: ${response.status} - ${errorText.substring(0, 200)}`);
}
diff --git a/examples/next-js/app/api/titan/[...path]/route.ts b/examples/next-js/app/api/titan/[...path]/route.ts
new file mode 100644
index 0000000..0e7c7aa
--- /dev/null
+++ b/examples/next-js/app/api/titan/[...path]/route.ts
@@ -0,0 +1,79 @@
+import { NextRequest, NextResponse } from 'next/server';
+
+// Demo endpoints by region
+const TITAN_DEMO_URLS: Record = {
+ us1: 'https://us1.api.demo.titan.exchange',
+ jp1: 'https://jp1.api.demo.titan.exchange',
+ de1: 'https://de1.api.demo.titan.exchange',
+};
+
+/**
+ * Next.js API route proxy for Titan API.
+ * Proxies all requests to Titan's API to work around CORS restrictions.
+ *
+ * Usage: /api/titan/api/v1/quote/swap?inputMint=...®ion=us1
+ * → proxied to https://us1.api.demo.titan.exchange/api/v1/quote/swap?inputMint=...
+ *
+ * Environment:
+ * - TITAN_API_TOKEN: Your Titan API JWT token (required for demo endpoints)
+ *
+ * Query params:
+ * - region: 'us1' | 'jp1' | 'de1' (default: 'us1') - selects Titan endpoint
+ * - ...all other params forwarded to Titan
+ */
+export async function GET(request: NextRequest, { params }: { params: Promise<{ path: string[] }> }) {
+ const { path } = await params;
+ const titanPath = '/' + path.join('/');
+ const searchParams = request.nextUrl.searchParams;
+
+ // Extract region (used for routing, not forwarded)
+ const region = searchParams.get('region') || 'us1';
+ const baseUrl = TITAN_DEMO_URLS[region] || TITAN_DEMO_URLS.us1;
+
+ // Build params to forward (exclude 'region')
+ const forwardParams = new URLSearchParams();
+ searchParams.forEach((value, key) => {
+ if (key !== 'region') {
+ forwardParams.set(key, value);
+ }
+ });
+
+ const url = forwardParams.toString() ? `${baseUrl}${titanPath}?${forwardParams}` : `${baseUrl}${titanPath}`;
+
+ try {
+ // Use server-side token from env, or forward client header as fallback
+ const authToken = process.env.TITAN_API_TOKEN || request.headers.get('Authorization')?.replace('Bearer ', '');
+ const headers: Record = {
+ Accept: 'application/msgpack',
+ };
+ if (authToken) {
+ headers['Authorization'] = `Bearer ${authToken}`;
+ }
+
+ const response = await fetch(url, { headers });
+
+ if (!response.ok) {
+ const errorText = await response.text().catch(() => 'Unknown error');
+ console.error(`Titan API error (${response.status}) at ${titanPath}:`, errorText);
+ return new NextResponse(errorText, {
+ status: response.status,
+ headers: { 'Content-Type': 'text/plain' },
+ });
+ }
+
+ // Return MessagePack binary response as-is
+ const buffer = await response.arrayBuffer();
+ return new NextResponse(buffer, {
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/msgpack',
+ },
+ });
+ } catch (error) {
+ console.error('Titan API proxy error:', error);
+ return new NextResponse(error instanceof Error ? error.message : 'Failed to proxy Titan request', {
+ status: 500,
+ headers: { 'Content-Type': 'text/plain' },
+ });
+ }
+}
diff --git a/examples/next-js/app/layout.tsx b/examples/next-js/app/layout.tsx
index 62e0fdc..33f7d96 100644
--- a/examples/next-js/app/layout.tsx
+++ b/examples/next-js/app/layout.tsx
@@ -72,7 +72,9 @@ export default function RootLayout({
}>) {
return (
-
+
{children}
diff --git a/examples/next-js/app/page.tsx b/examples/next-js/app/page.tsx
index 073b6f6..ed39c81 100644
--- a/examples/next-js/app/page.tsx
+++ b/examples/next-js/app/page.tsx
@@ -76,7 +76,7 @@ export default function Home() {
afterCode={afterCode}
/>
-
+
{/* Playground - sticky at top, covers content as it scrolls behind */}
diff --git a/examples/next-js/components/code/code-block.tsx b/examples/next-js/components/code/code-block.tsx
index be989e7..9a0de16 100644
--- a/examples/next-js/components/code/code-block.tsx
+++ b/examples/next-js/components/code/code-block.tsx
@@ -29,10 +29,7 @@ function CodeBlockFallback({
return (
);
}
-
diff --git a/examples/next-js/components/connector/wallet-modal.tsx b/examples/next-js/components/connector/wallet-modal.tsx
index 796ec40..a562214 100644
--- a/examples/next-js/components/connector/wallet-modal.tsx
+++ b/examples/next-js/components/connector/wallet-modal.tsx
@@ -146,7 +146,9 @@ export function WalletModal({ open, onOpenChange }: WalletModalProps) {
- {isConnecting &&
}
+ {isConnecting && (
+
+ )}
{walletInfo.wallet.icon && (
- {isConnecting &&
}
+ {isConnecting && (
+
+ )}
{walletInfo.wallet.icon && (
{walletInfo.wallet.name}
-
- Not installed
-
+ Not installed
@@ -301,9 +306,7 @@ export function WalletModal({ open, onOpenChange }: WalletModalProps) {
Install a Solana wallet extension to get started
-