Skip to content

MicroFlip - Domain Flipping Automation

High-speed domain auction monitoring and automated bidding system

typescript
import { cronflow } from 'cronflow';
import { z } from 'zod';

// Types and Interfaces
interface DomainAuction {
  id: string;
  domain: string;
  currentBid: number;
  timeRemaining: number;
  registrar: string;
  metrics: {
    length: number;
    hasKeywords: boolean;
    extension: string;
    age?: number;
    backlinks?: number;
    searchVolume?: number;
  };
}

interface AIValuation {
  estimatedValue: number;
  confidence: number;
  factors: {
    keyword_strength: number;
    brandability: number;
    seo_potential: number;
    market_demand: number;
  };
  recommendation: 'BUY' | 'PASS' | 'WATCH';
}

interface BidStrategy {
  maxBid: number;
  incrementStep: number;
  timeBuffer: number; // seconds before auction ends
}

// AI Valuation Function (Mock implementation - replace with real AI service)
async function aiValuateDomain(
  domain: string,
  metrics: DomainAuction['metrics']
): Promise<AIValuation> {
  // Simulate AI processing time (sub-100ms as required)
  await new Promise(resolve => setTimeout(resolve, Math.random() * 50));

  const factors = {
    keyword_strength: calculateKeywordStrength(domain),
    brandability: calculateBrandability(domain),
    seo_potential: calculateSEOPotential(metrics),
    market_demand: calculateMarketDemand(domain, metrics),
  };

  const weightedScore =
    factors.keyword_strength * 0.3 +
    factors.brandability * 0.25 +
    factors.seo_potential * 0.25 +
    factors.market_demand * 0.2;

  const estimatedValue = Math.max(100, weightedScore * 1000);
  const confidence = Math.min(0.95, weightedScore);

  let recommendation: AIValuation['recommendation'] = 'PASS';
  if (weightedScore > 0.7 && confidence > 0.8) recommendation = 'BUY';
  else if (weightedScore > 0.5) recommendation = 'WATCH';

  return {
    estimatedValue,
    confidence,
    factors,
    recommendation,
  };
}

// Helper functions for AI valuation
function calculateKeywordStrength(domain: string): number {
  const keywords = [
    'app',
    'tech',
    'ai',
    'crypto',
    'shop',
    'store',
    'pro',
    'hub',
  ];
  const hasKeyword = keywords.some(kw => domain.toLowerCase().includes(kw));
  return hasKeyword ? 0.8 : Math.random() * 0.5;
}

function calculateBrandability(domain: string): number {
  const cleanDomain = domain.replace(/\.(com|net|org|io)$/, '');
  if (cleanDomain.length < 6) return 0.9;
  if (cleanDomain.length < 10) return 0.7;
  return 0.4;
}

function calculateSEOPotential(metrics: DomainAuction['metrics']): number {
  let score = 0.5;
  if (metrics.age && metrics.age > 5) score += 0.2;
  if (metrics.backlinks && metrics.backlinks > 100) score += 0.2;
  if (metrics.searchVolume && metrics.searchVolume > 1000) score += 0.1;
  return Math.min(1, score);
}

function calculateMarketDemand(
  domain: string,
  metrics: DomainAuction['metrics']
): number {
  // Premium extensions get higher scores
  const extensionScores: { [key: string]: number } = {
    com: 1.0,
    io: 0.8,
    ai: 0.9,
    app: 0.7,
    net: 0.6,
    org: 0.5,
  };

  return extensionScores[metrics.extension] || 0.3;
}

// Auction monitoring functions
async function fetchActiveAuctions(): Promise<DomainAuction[]> {
  // Mock implementation - replace with real auction APIs
  // (GoDaddy Auctions, NameJet, SnapNames, etc.)
  const mockAuctions: DomainAuction[] = [
    {
      id: 'auction_001',
      domain: 'techapp.com',
      currentBid: 500,
      timeRemaining: 3600,
      registrar: 'GoDaddy',
      metrics: {
        length: 7,
        hasKeywords: true,
        extension: 'com',
        age: 8,
        backlinks: 150,
        searchVolume: 2500,
      },
    },
    {
      id: 'auction_002',
      domain: 'cryptohub.io',
      currentBid: 300,
      timeRemaining: 1800,
      registrar: 'NameJet',
      metrics: {
        length: 9,
        hasKeywords: true,
        extension: 'io',
        backlinks: 50,
        searchVolume: 1200,
      },
    },
  ];

  return mockAuctions;
}

async function placeBid(
  auctionId: string,
  bidAmount: number,
  registrar: string
): Promise<boolean> {
  // Mock implementation - replace with real registrar APIs
  console.log(
    `🎯 Placing bid: $${bidAmount} on auction ${auctionId} via ${registrar}`
  );

  // Simulate API call
  await new Promise(resolve => setTimeout(resolve, 100));

  // Mock success rate (90%)
  return Math.random() > 0.1;
}

function calculateBidStrategy(
  auction: DomainAuction,
  valuation: AIValuation
): BidStrategy | null {
  if (valuation.recommendation !== 'BUY') return null;

  const profitMargin = 0.3; // 30% profit margin
  const maxBid = Math.floor(valuation.estimatedValue * (1 - profitMargin));

  if (maxBid <= auction.currentBid) return null;

  return {
    maxBid,
    incrementStep: Math.max(10, Math.floor(maxBid * 0.05)), // 5% increments
    timeBuffer: 30, // Place bids 30 seconds before auction ends
  };
}

// Webhook schema for manual triggers
const auctionWebhookSchema = z.object({
  action: z.enum(['scan', 'force_bid', 'stop_monitoring']),
  domain: z.string().optional(),
  max_bid: z.number().optional(),
});

// Define the MicroFlip workflow
const microFlipWorkflow = cronflow.define({
  id: 'microflip-domain-automation',
  name: 'MicroFlip - Domain Flipping Automation',
  description:
    'High-speed domain auction monitoring and automated bidding system',
  hooks: {
    onSuccess: (ctx, stepId) => {
      if (!stepId) {
        console.log('🎉 MicroFlip workflow completed successfully!');
        console.log('📊 Final results:', ctx.last);
      }
    },
    onFailure: (ctx, stepId) => {
      console.error(`❌ MicroFlip failure in step ${stepId}:`, ctx.step_error);
      // Could add alerting here (email, Slack, etc.)
    },
  },
});

// Main auction monitoring workflow (triggered every 30 seconds)
microFlipWorkflow
  .onSchedule('*/30 * * * * *') // Every 30 seconds
  .step('fetch-auctions', async ctx => {
    const startTime = Date.now();
    const auctions = await fetchActiveAuctions();
    const fetchTime = Date.now() - startTime;

    console.log(
      `📡 Fetched ${auctions.length} active auctions in ${fetchTime}ms`
    );

    return {
      auctions,
      fetchTime,
      timestamp: new Date().toISOString(),
    };
  })
  .step('filter-urgent-auctions', async ctx => {
    // Filter auctions ending soon (next 5 minutes)
    const urgentAuctions = ctx.last.auctions.filter(
      (auction: DomainAuction) => auction.timeRemaining <= 300
    );

    console.log(
      `⏰ Found ${urgentAuctions.length} urgent auctions (ending within 5 minutes)`
    );

    return {
      urgentAuctions,
      totalAuctions: ctx.last.auctions.length,
    };
  })
  .if('has-urgent-auctions', ctx => ctx.last.urgentAuctions.length > 0)
  .step('process-urgent-auctions', async ctx => {
    const { urgentAuctions } = ctx.last;

    // Process up to 10 urgent auctions in parallel for maximum speed
    const auctionPromises = urgentAuctions
      .slice(0, 10)
      .map((auction: DomainAuction) => processAuctionForBidding(auction));

    const results = await Promise.all(auctionPromises);

    return {
      results,
      totalProcessed: results.length,
    };
  })
  .step('aggregate-bid-results', async ctx => {
    const { results } = ctx.last;
    const successfulBids = results.filter((r: any) => r?.bidPlaced).length;
    const totalProcessed = results.length;

    console.log(
      `📈 Processed ${totalProcessed} auctions, placed ${successfulBids} bids`
    );

    return {
      totalProcessed,
      successfulBids,
      results,
    };
  })
  .endIf()
  .action('log-monitoring-cycle', ctx => {
    const stats = {
      totalAuctions: ctx.last.totalAuctions || 0,
      urgentAuctions: ctx.last.urgentAuctions?.length || 0,
      bidsPlaced: ctx.last.successfulBids || 0,
      timestamp: new Date().toISOString(),
    };

    console.log('📊 Monitoring cycle stats:', stats);
  });

// Helper function to process individual auctions
async function processAuctionForBidding(auction: DomainAuction) {
  try {
    const startTime = Date.now();

    // AI valuation (must be <100ms)
    const valuation = await aiValuateDomain(auction.domain, auction.metrics);
    const valuationTime = Date.now() - startTime;

    if (valuationTime > 100) {
      console.warn(
        `⚠️ AI valuation took ${valuationTime}ms for ${auction.domain} (target: <100ms)`
      );
    }

    // Calculate bid strategy
    const bidStrategy = calculateBidStrategy(auction, valuation);

    if (!bidStrategy) {
      return {
        domain: auction.domain,
        action: 'skipped',
        reason:
          valuation.recommendation === 'PASS' ? 'Low valuation' : 'Bid too low',
        valuation,
        processingTime: Date.now() - startTime,
      };
    }

    // Place bid if profitable and time is right
    const shouldBid =
      auction.timeRemaining <= bidStrategy.timeBuffer &&
      auction.currentBid < bidStrategy.maxBid;

    if (shouldBid) {
      const bidAmount = Math.min(
        bidStrategy.maxBid,
        auction.currentBid + bidStrategy.incrementStep
      );

      const bidSuccess = await placeBid(
        auction.id,
        bidAmount,
        auction.registrar
      );

      return {
        domain: auction.domain,
        action: 'bid_placed',
        bidAmount,
        maxBid: bidStrategy.maxBid,
        estimatedValue: valuation.estimatedValue,
        confidence: valuation.confidence,
        bidPlaced: bidSuccess,
        processingTime: Date.now() - startTime,
      };
    }

    return {
      domain: auction.domain,
      action: 'watching',
      timeRemaining: auction.timeRemaining,
      maxBid: bidStrategy.maxBid,
      currentBid: auction.currentBid,
      processingTime: Date.now() - startTime,
    };
  } catch (error) {
    console.error(`❌ Error processing auction for ${auction.domain}:`, error);
    return {
      domain: auction.domain,
      action: 'error',
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}

// Manual control webhook for testing and overrides
microFlipWorkflow
  .onWebhook('/webhooks/microflip-control', {
    schema: auctionWebhookSchema,
  })
  .step('handle-manual-action', async ctx => {
    const { action, domain, max_bid } = ctx.payload;

    switch (action) {
      case 'scan':
        console.log('🔍 Manual scan triggered');
        const auctions = await fetchActiveAuctions();
        return {
          action,
          result: `Found ${auctions.length} auctions`,
          auctions,
        };

      case 'force_bid':
        if (!domain || !max_bid) {
          throw new Error('Domain and max_bid required for force_bid action');
        }
        console.log(
          `💰 Force bid triggered for ${domain} with max bid $${max_bid}`
        );
        // Implementation would find the auction and place bid
        return { action, domain, max_bid, result: 'Force bid initiated' };

      case 'stop_monitoring':
        console.log('🛑 Monitoring stopped via webhook');
        return { action, result: 'Monitoring stopped' };

      default:
        throw new Error(`Unknown action: ${action}`);
    }
  })
  .action('log-manual-action', ctx => {
    console.log('📝 Manual action completed:', ctx.last);
  });

// Performance monitoring workflow (runs every minute)
const performanceMonitor = cronflow.define({
  id: 'microflip-performance-monitor',
  name: 'MicroFlip Performance Monitor',
  description: 'Monitor system performance and auction processing metrics',
});

performanceMonitor
  .onSchedule('0 * * * * *') // Every minute
  .step('collect-metrics', async ctx => {
    // Mock metrics collection - replace with real monitoring
    const metrics = {
      timestamp: new Date().toISOString(),
      memory_usage: process.memoryUsage(),
      active_auctions: Math.floor(Math.random() * 100),
      avg_valuation_time: Math.floor(Math.random() * 80) + 20, // 20-100ms
      successful_bids_last_hour: Math.floor(Math.random() * 10),
      profit_last_24h: Math.floor(Math.random() * 5000),
    };

    return metrics;
  })
  .if('performance-alert', ctx => ctx.last.avg_valuation_time > 90)
  .step('send-performance-alert', async ctx => {
    console.warn('⚠️ Performance Alert: AI valuation time exceeding 90ms');
    console.warn('Current average:', ctx.last.avg_valuation_time, 'ms');

    // Here you could send alerts via email, Slack, etc.
    return { alert_sent: true, reason: 'High valuation time' };
  })
  .endIf()
  .action('log-performance', ctx => {
    if (ctx.last.alert_sent) {
      console.log('📊 Performance metrics (ALERT):', ctx.last);
    } else {
      console.log('📊 Performance metrics:', ctx.last);
    }
  });

// Start the workflows
console.log('🚀 Starting MicroFlip Domain Flipping Automation...');
console.log('📡 Monitoring auctions every 30 seconds');
console.log('🎯 AI valuation target: <100ms per domain');
console.log('💰 Auto-bidding on profitable opportunities');
console.log('');
console.log('Manual controls available at:');
console.log('POST /webhooks/microflip-control');
console.log('  - {"action": "scan"} - Force auction scan');
console.log(
  '  - {"action": "force_bid", "domain": "example.com", "max_bid": 1000}'
);
console.log('  - {"action": "stop_monitoring"}');

export { microFlipWorkflow, performanceMonitor };

Released under the Apache 2.0 License.