/** * Advanced Auction System for Dynamic Pricing * Supports multiple auction types for AI agent services and marketplace items */ import type { Address } from '@solana/addresses'; import type { Rpc, SolanaRpcApi } from '@solana/rpc'; import type { Commitment } from '@solana/rpc-types'; import type { KeyPairSigner } from '@solana/signers'; import { sendAndConfirmTransactionFactory } from '../utils/transaction-helpers.js'; /** * Auction types supported by the system */ export type AuctionType = | 'english' // Traditional ascending bid auction | 'dutch' // Descending price auction | 'sealed_bid' // Sealed bid auction | 'reverse' // Reverse auction (buyers compete) | 'vickrey' // Second-price sealed bid | 'candle' // Random end time auction | 'reserve' // Auction with reserve price | 'buy_now'; // Auction with immediate purchase option /** * Auction status states */ export type AuctionStatus = | 'created' // Auction created but not started | 'active' // Currently accepting bids | 'ending' // In final countdown period | 'ended' // Auction completed | 'cancelled' // Auction cancelled | 'settled' // Winner determined and payment processed | 'disputed'; // Under dispute resolution /** * Bid information */ export interface IAuctionBid { bidId: Address; bidder: Address; amount: bigint; timestamp: number; isWinning: boolean; bidData?: { maxBid?: bigint; // For proxy bidding conditions?: string[]; // Special bid conditions autoIncrement?: bigint; // Auto-increment amount }; } /** * Comprehensive auction configuration */ export interface IAuctionConfig { // Basic auction settings auctionType: AuctionType; title: string; description: string; category: string; // Item being auctioned itemType: 'agent' | 'service' | 'nft' | 'bulk_package' | 'subscription'; itemId: Address; itemMetadata: { name: string; description: string; imageUri?: string; attributes?: Record; }; // Pricing and economic settings startingPrice: bigint; reservePrice?: bigint; buyNowPrice?: bigint; minimumIncrement: bigint; paymentToken: Address; // Timing configuration startTime: number; duration: number; // Duration in milliseconds extensionTrigger?: number; // Extend if bid in last X ms extensionTime?: number; // How much to extend by // Advanced features allowProxyBidding: boolean; requireDeposit: boolean; depositAmount?: bigint; maxBidsPerUser?: number; // Access control isPrivate: boolean; whitelist?: Address[]; blacklist?: Address[]; minimumReputation?: number; // Special conditions multiWinner?: { enabled: boolean; maxWinners: number; winnerSelection: 'highest_bids' | 'lottery' | 'proportional'; }; } /** * Complete auction state */ export interface IAuction { auctionId: Address; seller: Address; config: IAuctionConfig; status: AuctionStatus; // Current auction state currentPrice: bigint; highestBid?: IAuctionBid; totalBids: number; uniqueBidders: number; // Timing information createdAt: number; startedAt?: number; endsAt: number; actualEndTime?: number; // Financial tracking totalVolume: bigint; escrowAmount: bigint; feesCollected: bigint; // Participants bidders: Address[]; watchers: Address[]; // Resolution winners?: Array<{ bidder: Address; winningBid: bigint; rank: number; }>; // Metadata viewCount: number; socialEngagement: { likes: number; shares: number; comments: number; }; } /** * Auction analytics and insights */ export interface IAuctionAnalytics { // Performance metrics participationRate: number; averageBidIncrement: bigint; bidFrequency: number; // Bids per hour priceAppreciation: number; // Percentage increase // Bidder behavior bidderTypes: { aggressive: number; // High increment bidders conservative: number; // Low increment bidders lastMinute: number; // Bid in final moments proxy: number; // Using proxy bidding }; // Market insights marketComparison: { similarAuctions: number; averagePrice: bigint; priceVariance: number; }; // Predictions predictedEndPrice?: bigint; demandLevel: 'low' | 'medium' | 'high' | 'exceptional'; recommendedActions: string[]; } /** * Auction search and filtering options */ export interface IAuctionFilters { // Status and timing statuses?: AuctionStatus[]; endingWithin?: number; // Milliseconds startedAfter?: number; // Economic filters minCurrentPrice?: bigint; maxCurrentPrice?: bigint; paymentTokens?: Address[]; hasBuyNow?: boolean; hasReserve?: boolean; // Item and category filters categories?: string[]; itemTypes?: IAuctionConfig['itemType'][]; sellerReputation?: { min: number; max?: number }; // Auction type filters auctionTypes?: AuctionType[]; features?: Array<'proxy_bidding' | 'multi_winner' | 'private' | 'verified_seller'>; // Sorting options sortBy?: 'ending_soon' | 'newest' | 'price_low' | 'price_high' | 'most_bids' | 'most_watched'; sortOrder?: 'asc' | 'desc'; } /** * Advanced Auction Service */ export class AuctionService { constructor( private readonly rpc: Rpc, private readonly _programId: Address, private readonly commitment: Commitment = 'confirmed' ) {} /** * Create a new auction with comprehensive configuration */ async createAuction( seller: KeyPairSigner, config: IAuctionConfig ): Promise<{ auctionId: Address; signature: string; }> { try { console.log(`🏛️ Creating ${config.auctionType} auction: ${config.title}`); // Validate auction configuration this.validateAuctionConfig(config); // Generate auction PDA const auctionId = `auction_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` as Address; // In a real implementation, this would call a createAuction smart contract instruction const mockInstruction = { programAddress: this._programId, accounts: [ { address: auctionId, role: 'writable' as const }, { address: seller.address, role: 'writable_signer' as const }, ], data: new Uint8Array([1, 2, 3]) // Mock instruction data }; // Build and send transaction const sendTransactionFactory = sendAndConfirmTransactionFactory('https://api.devnet.solana.com'); const result = await sendTransactionFactory([mockInstruction], [seller]); const signature = result.signature; console.log('✅ Auction created:', { auctionId, signature }); return { auctionId, signature }; } catch (error) { throw new Error(`Auction creation failed: ${String(error)}`); } } /** * Place a bid on an active auction */ async placeBid( bidder: KeyPairSigner, auctionId: Address, bidAmount: bigint, bidOptions: { maxBid?: bigint; // For proxy bidding conditions?: string[]; // Special conditions autoIncrement?: bigint; // Auto-increment settings } = {} ): Promise<{ bidId: Address; signature: string; isWinning: boolean; nextMinimumBid: bigint; }> { try { console.log(`💰 Placing bid of ${bidAmount} on auction: ${auctionId}`); // Get current auction state const auction = await this.getAuction(auctionId); if (!auction) { throw new Error('Auction not found'); } // Validate bid this.validateBid(auction, bidder.address, bidAmount, bidOptions); // Generate bid ID const bidId = `bid_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` as Address; // Determine if this is the winning bid const isWinning = this.calculateWinningStatus(auction, bidAmount); const nextMinimumBid = this.calculateNextMinimumBid(auction, bidAmount); // In a real implementation, this would call a placeBid smart contract instruction const mockInstruction = { programAddress: this._programId, accounts: [ { address: bidId, role: 'writable' as const }, { address: auctionId, role: 'writable' as const }, { address: bidder.address, role: 'writable_signer' as const }, ], data: new Uint8Array([2, 3, 4]) // Mock instruction data }; const sendTransactionFactory = sendAndConfirmTransactionFactory('https://api.devnet.solana.com'); const result = await sendTransactionFactory([mockInstruction], [bidder]); const signature = result.signature; console.log('✅ Bid placed:', { bidId, signature, isWinning }); return { bidId, signature, isWinning, nextMinimumBid }; } catch (error) { throw new Error(`Bid placement failed: ${String(error)}`); } } /** * Execute buy-now purchase for auctions with immediate purchase option */ async buyNow( buyer: KeyPairSigner, auctionId: Address ): Promise<{ transactionId: Address; signature: string; finalPrice: bigint; }> { try { console.log(`⚡ Executing buy-now for auction: ${auctionId}`); const auction = await this.getAuction(auctionId); if (!auction) { throw new Error('Auction not found'); } if (!auction.config.buyNowPrice) { throw new Error('This auction does not support buy-now'); } if (auction.status !== 'active') { throw new Error('Auction is not active'); } const transactionId = `txn_buynow_${Date.now()}` as Address; // In a real implementation, this would end the auction and process payment const mockInstruction = { programAddress: this._programId, accounts: [ { address: transactionId, role: 'writable' as const }, { address: auctionId, role: 'writable' as const }, { address: buyer.address, role: 'writable_signer' as const }, ], data: new Uint8Array([3, 4, 5]) // Mock instruction data }; const sendTransactionFactory = sendAndConfirmTransactionFactory('https://api.devnet.solana.com'); const result = await sendTransactionFactory([mockInstruction], [buyer]); const signature = result.signature; console.log('✅ Buy-now executed:', { transactionId, signature }); return { transactionId, signature, finalPrice: auction.config.buyNowPrice }; } catch (error) { throw new Error(`Buy-now execution failed: ${String(error)}`); } } /** * End an auction (for seller or automatic ending) */ async endAuction( authority: KeyPairSigner, auctionId: Address, reason: 'time_expired' | 'buy_now' | 'reserve_not_met' | 'seller_cancelled' = 'time_expired' ): Promise<{ signature: string; winners: Array<{ bidder: Address; winningBid: bigint; rank: number; }>; totalPayout: bigint; }> { try { console.log(`🏁 Ending auction: ${auctionId} (reason: ${reason})`); const auction = await this.getAuction(auctionId); if (!auction) { throw new Error('Auction not found'); } // Determine winners based on auction type and configuration const winners = await this.determineWinners(auction); const totalPayout = winners.reduce((sum, winner) => sum + winner.winningBid, 0n); // In a real implementation, this would call endAuction instruction and process payments const mockInstruction = { programAddress: this._programId, accounts: [ { address: auctionId, role: 'writable' as const }, { address: authority.address, role: 'writable_signer' as const }, ], data: new Uint8Array([4, 5, 6]) // Mock instruction data }; const sendTransactionFactory = sendAndConfirmTransactionFactory('https://api.devnet.solana.com'); const result = await sendTransactionFactory([mockInstruction], [authority]); const signature = result.signature; console.log('✅ Auction ended:', { signature, winners: winners.length, totalPayout }); return { signature, winners, totalPayout }; } catch (error) { throw new Error(`Auction ending failed: ${String(error)}`); } } /** * Get detailed auction information */ async getAuction(auctionId: Address): Promise { try { // In a real implementation, this would fetch from blockchain const accountInfo = await this.rpc .getAccountInfo(auctionId, { commitment: this.commitment, encoding: 'base64', }) .send(); if (!accountInfo.value) { return null; } // Simulate auction data parsing return this.generateMockAuction(auctionId); } catch (error) { console.error('Failed to get auction:', error); return null; } } /** * Search and filter auctions with advanced criteria */ async searchAuctions( filters: IAuctionFilters = {}, limit: number = 50, offset: number = 0 ): Promise<{ auctions: IAuction[]; totalCount: number; hasMore: boolean; searchMetadata: { filters: IAuctionFilters; executionTime: number; qualityScore: number; }; }> { const startTime = Date.now(); try { console.log('🔍 Searching auctions with filters:', filters); // Get all auctions (in production, this would use efficient indexing) const allAuctions = await this.getAllAuctions(1000); // Apply filters let filteredAuctions = this.applyAuctionFilters(allAuctions, filters); // Apply sorting filteredAuctions = this.sortAuctions(filteredAuctions, filters); // Apply pagination const totalCount = filteredAuctions.length; const paginatedAuctions = filteredAuctions.slice(offset, offset + limit); const executionTime = Date.now() - startTime; const qualityScore = this.calculateSearchQuality(paginatedAuctions, filters); return { auctions: paginatedAuctions, totalCount, hasMore: offset + limit < totalCount, searchMetadata: { filters, executionTime, qualityScore, }, }; } catch (error) { throw new Error(`Auction search failed: ${String(error)}`); } } /** * Get comprehensive auction analytics */ async getAuctionAnalytics(auctionId: Address): Promise { try { console.log(`📊 Generating analytics for auction: ${auctionId}`); const auction = await this.getAuction(auctionId); if (!auction) { throw new Error('Auction not found'); } // Get all bids for analysis const bids = await this.getAuctionBids(auctionId); // Calculate performance metrics const participationRate = this.calculateParticipationRate(auction, bids); const averageBidIncrement = this.calculateAverageBidIncrement(bids); const bidFrequency = this.calculateBidFrequency(auction, bids); const priceAppreciation = this.calculatePriceAppreciation(auction); // Analyze bidder behavior const bidderTypes = this.analyzeBidderTypes(bids); // Generate market insights const marketComparison = await this.getMarketComparison(auction); // Make predictions const predictedEndPrice = this.predictEndPrice(auction, bids); const demandLevel = this.assessDemandLevel(auction, bids); const recommendedActions = this.generateRecommendations(auction, bids); return { participationRate, averageBidIncrement, bidFrequency, priceAppreciation, bidderTypes, marketComparison, predictedEndPrice, demandLevel, recommendedActions, }; } catch (error) { throw new Error(`Analytics generation failed: ${String(error)}`); } } /** * Get trending auctions based on activity and engagement */ async getTrendingAuctions( category?: string, limit: number = 20 ): Promise { try { console.log(`🔥 Getting trending auctions for category: ${category || 'all'}`); const filters: IAuctionFilters = { statuses: ['active', 'ending'], sortBy: 'most_bids', sortOrder: 'desc', }; if (category) { filters.categories = [category]; } const result = await this.searchAuctions(filters, limit); return result.auctions.filter(auction => this.calculateTrendingScore(auction) > 70 ); } catch (error) { throw new Error(`Trending auctions retrieval failed: ${String(error)}`); } } /** * Get auctions ending soon */ async getEndingSoonAuctions( withinMinutes: number = 60, limit: number = 20 ): Promise { try { console.log(`⏰ Getting auctions ending within ${withinMinutes} minutes`); const filters: IAuctionFilters = { statuses: ['active', 'ending'], endingWithin: withinMinutes * 60 * 1000, sortBy: 'ending_soon', sortOrder: 'asc', }; const result = await this.searchAuctions(filters, limit); return result.auctions; } catch (error) { throw new Error(`Ending soon auctions retrieval failed: ${String(error)}`); } } /** * Private helper methods */ private validateAuctionConfig(config: IAuctionConfig): void { if (!config.title || config.title.length < 3) { throw new Error('Auction title must be at least 3 characters'); } if (config.startingPrice <= 0n) { throw new Error('Starting price must be positive'); } if (config.reservePrice && config.reservePrice < config.startingPrice) { throw new Error('Reserve price cannot be less than starting price'); } if (config.buyNowPrice && config.buyNowPrice <= config.startingPrice) { throw new Error('Buy-now price must be higher than starting price'); } if (config.duration < 60000) { // Minimum 1 minute throw new Error('Auction duration must be at least 1 minute'); } if (config.startTime < Date.now() - 60000) { throw new Error('Start time cannot be in the past'); } } private validateBid( auction: IAuction, bidder: Address, bidAmount: bigint, bidOptions: any ): void { if (auction.status !== 'active') { throw new Error('Auction is not active'); } if (auction.seller === bidder) { throw new Error('Seller cannot bid on their own auction'); } if (Date.now() > auction.endsAt) { throw new Error('Auction has ended'); } const minimumBid = this.calculateNextMinimumBid(auction, auction.currentPrice); if (bidAmount < minimumBid) { throw new Error(`Bid must be at least ${minimumBid}`); } if (auction.config.isPrivate && auction.config.whitelist) { if (!auction.config.whitelist.includes(bidder)) { throw new Error('Bidder not whitelisted for private auction'); } } if (auction.config.blacklist?.includes(bidder)) { throw new Error('Bidder is blacklisted from this auction'); } } private calculateWinningStatus(auction: IAuction, bidAmount: bigint): boolean { return !auction.highestBid || bidAmount > auction.highestBid.amount; } private calculateNextMinimumBid(auction: IAuction, currentBid: bigint): bigint { return currentBid + auction.config.minimumIncrement; } private async determineWinners(auction: IAuction): Promise> { // Simulate winner determination if (!auction.highestBid) { return []; } const winners = [{ bidder: auction.highestBid.bidder, winningBid: auction.highestBid.amount, rank: 1, }]; // Handle multi-winner auctions if (auction.config.multiWinner?.enabled && auction.config.multiWinner.maxWinners > 1) { // Add additional winners based on strategy const additionalWinners = Math.min( auction.config.multiWinner.maxWinners - 1, auction.uniqueBidders - 1 ); for (let i = 0; i < additionalWinners; i++) { winners.push({ bidder: `winner_${i + 2}` as Address, winningBid: auction.highestBid.amount - BigInt((i + 1) * 100000000), rank: i + 2, }); } } return winners; } private generateMockAuction(auctionId: Address): IAuction { const now = Date.now(); const auctionTypes: AuctionType[] = ['english', 'dutch', 'sealed_bid', 'reverse']; const itemTypes = ['agent', 'service', 'nft', 'bulk_package'] as const; const randomType = auctionTypes[Math.floor(Math.random() * auctionTypes.length)]; const randomItemType = itemTypes[Math.floor(Math.random() * itemTypes.length)]; return { auctionId, seller: `seller_${Date.now()}` as Address, config: { auctionType: randomType, title: `Premium ${randomItemType} Auction`, description: `High-quality ${randomItemType} available for bidding`, category: 'AI Services', itemType: randomItemType, itemId: `item_${Date.now()}` as Address, itemMetadata: { name: `Premium ${randomItemType}`, description: 'Top-tier AI service with advanced capabilities', imageUri: 'https://example.com/image.jpg', }, startingPrice: BigInt(Math.floor(Math.random() * 1000000000) + 100000000), reservePrice: BigInt(Math.floor(Math.random() * 2000000000) + 500000000), buyNowPrice: BigInt(Math.floor(Math.random() * 3000000000) + 1000000000), minimumIncrement: BigInt(50000000), paymentToken: 'So11111111111111111111111111111111111111112' as Address, startTime: now - Math.random() * 3600000, duration: Math.floor(Math.random() * 86400000) + 3600000, allowProxyBidding: Math.random() > 0.5, requireDeposit: Math.random() > 0.7, depositAmount: BigInt(Math.floor(Math.random() * 100000000)), isPrivate: Math.random() > 0.8, }, status: Math.random() > 0.3 ? 'active' : 'ending', currentPrice: BigInt(Math.floor(Math.random() * 1500000000) + 200000000), totalBids: Math.floor(Math.random() * 50) + 1, uniqueBidders: Math.floor(Math.random() * 20) + 1, createdAt: now - Math.random() * 86400000, startedAt: now - Math.random() * 3600000, endsAt: now + Math.random() * 3600000, totalVolume: BigInt(Math.floor(Math.random() * 5000000000)), escrowAmount: BigInt(Math.floor(Math.random() * 1000000000)), feesCollected: BigInt(Math.floor(Math.random() * 50000000)), bidders: [], watchers: [], viewCount: Math.floor(Math.random() * 1000) + 10, socialEngagement: { likes: Math.floor(Math.random() * 100), shares: Math.floor(Math.random() * 50), comments: Math.floor(Math.random() * 30), }, }; } private async getAllAuctions(limit: number): Promise { // Simulate getting auctions from blockchain return Array.from({ length: Math.min(limit, 30) }, (_, i) => this.generateMockAuction(`auction_${i + 1}_${Date.now()}` as Address) ); } private applyAuctionFilters(auctions: IAuction[], filters: IAuctionFilters): IAuction[] { return auctions.filter(auction => { // Status filtering if (filters.statuses && !filters.statuses.includes(auction.status)) return false; // Price filtering if (filters.minCurrentPrice && auction.currentPrice < filters.minCurrentPrice) return false; if (filters.maxCurrentPrice && auction.currentPrice > filters.maxCurrentPrice) return false; // Category filtering if (filters.categories && !filters.categories.includes(auction.config.category)) return false; // Item type filtering if (filters.itemTypes && !filters.itemTypes.includes(auction.config.itemType)) return false; // Auction type filtering if (filters.auctionTypes && !filters.auctionTypes.includes(auction.config.auctionType)) return false; // Timing filtering if (filters.endingWithin && auction.endsAt - Date.now() > filters.endingWithin) return false; if (filters.startedAfter && (!auction.startedAt || auction.startedAt < filters.startedAfter)) return false; // Feature filtering if (filters.features) { if (filters.features.includes('proxy_bidding') && !auction.config.allowProxyBidding) return false; if (filters.features.includes('private') && !auction.config.isPrivate) return false; if (filters.features.includes('multi_winner') && !auction.config.multiWinner?.enabled) return false; } return true; }); } private sortAuctions(auctions: IAuction[], filters: IAuctionFilters): IAuction[] { const sortedAuctions = [...auctions]; switch (filters.sortBy) { case 'ending_soon': sortedAuctions.sort((a, b) => a.endsAt - b.endsAt); break; case 'newest': sortedAuctions.sort((a, b) => b.createdAt - a.createdAt); break; case 'price_low': sortedAuctions.sort((a, b) => Number(a.currentPrice - b.currentPrice)); break; case 'price_high': sortedAuctions.sort((a, b) => Number(b.currentPrice - a.currentPrice)); break; case 'most_bids': sortedAuctions.sort((a, b) => b.totalBids - a.totalBids); break; case 'most_watched': sortedAuctions.sort((a, b) => b.watchers.length - a.watchers.length); break; default: sortedAuctions.sort((a, b) => a.endsAt - b.endsAt); } if (filters.sortOrder === 'desc' && filters.sortBy !== 'price_high' && filters.sortBy !== 'most_bids' && filters.sortBy !== 'most_watched') { sortedAuctions.reverse(); } return sortedAuctions; } private calculateSearchQuality(auctions: IAuction[], filters: IAuctionFilters): number { if (auctions.length === 0) return 0; const diversityScore = Math.min(auctions.length / 20, 1) * 30; const relevanceScore = auctions.length > 0 ? 70 : 0; return Math.round(diversityScore + relevanceScore); } private async getAuctionBids(auctionId: Address): Promise { // Simulate getting bids for an auction const bidCount = Math.floor(Math.random() * 20) + 1; return Array.from({ length: bidCount }, (_, i) => ({ bidId: `bid_${i + 1}_${Date.now()}` as Address, bidder: `bidder_${i + 1}` as Address, amount: BigInt(Math.floor(Math.random() * 1000000000) + 100000000), timestamp: Date.now() - Math.random() * 3600000, isWinning: i === bidCount - 1, })); } private calculateParticipationRate(auction: IAuction, bids: IAuctionBid[]): number { return Math.min((auction.uniqueBidders / auction.viewCount) * 100, 100); } private calculateAverageBidIncrement(bids: IAuctionBid[]): bigint { if (bids.length < 2) return 0n; const sortedBids = bids.sort((a, b) => a.timestamp - b.timestamp); let totalIncrement = 0n; for (let i = 1; i < sortedBids.length; i++) { totalIncrement += sortedBids[i].amount - sortedBids[i - 1].amount; } return totalIncrement / BigInt(sortedBids.length - 1); } private calculateBidFrequency(auction: IAuction, bids: IAuctionBid[]): number { if (!auction.startedAt || bids.length === 0) return 0; const hoursActive = (Date.now() - auction.startedAt) / (1000 * 60 * 60); return bids.length / Math.max(hoursActive, 0.1); } private calculatePriceAppreciation(auction: IAuction): number { if (auction.config.startingPrice === 0n) return 0; const appreciation = Number(auction.currentPrice - auction.config.startingPrice); const starting = Number(auction.config.startingPrice); return (appreciation / starting) * 100; } private analyzeBidderTypes(bids: IAuctionBid[]): IAuctionAnalytics['bidderTypes'] { // Simulate bidder type analysis return { aggressive: Math.floor(bids.length * 0.2), conservative: Math.floor(bids.length * 0.5), lastMinute: Math.floor(bids.length * 0.2), proxy: Math.floor(bids.length * 0.1), }; } private async getMarketComparison(auction: IAuction): Promise { // Simulate market comparison return { similarAuctions: Math.floor(Math.random() * 50) + 10, averagePrice: BigInt(Math.floor(Math.random() * 1000000000) + 500000000), priceVariance: Math.random() * 0.5 + 0.1, }; } private predictEndPrice(auction: IAuction, bids: IAuctionBid[]): bigint { // Simple prediction based on current trajectory const appreciationRate = this.calculatePriceAppreciation(auction) / 100; const timeRemaining = (auction.endsAt - Date.now()) / (auction.endsAt - (auction.startedAt || auction.createdAt)); const predictedAppreciation = appreciationRate * (1 + timeRemaining * 0.5); return auction.config.startingPrice + BigInt(Math.floor(Number(auction.config.startingPrice) * predictedAppreciation)); } private assessDemandLevel(auction: IAuction, bids: IAuctionBid[]): IAuctionAnalytics['demandLevel'] { const bidFrequency = this.calculateBidFrequency(auction, bids); const participationRate = this.calculateParticipationRate(auction, bids); if (bidFrequency > 5 && participationRate > 15) return 'exceptional'; if (bidFrequency > 2 && participationRate > 10) return 'high'; if (bidFrequency > 1 && participationRate > 5) return 'medium'; return 'low'; } private generateRecommendations(auction: IAuction, bids: IAuctionBid[]): string[] { const recommendations: string[] = []; const demandLevel = this.assessDemandLevel(auction, bids); if (demandLevel === 'low') { recommendations.push('Consider lowering reserve price to attract more bidders'); recommendations.push('Improve item description and add more images'); } if (auction.endsAt - Date.now() < 3600000) { // Less than 1 hour recommendations.push('Consider extending auction duration to maximize final price'); } if (auction.config.buyNowPrice && auction.currentPrice > auction.config.buyNowPrice * 80n / 100n) { recommendations.push('Buy-now price may be reached soon - monitor closely'); } return recommendations; } private calculateTrendingScore(auction: IAuction): number { const bidActivity = Math.min((auction.totalBids / 20) * 40, 40); const timeRemaining = Math.max(0, Math.min((auction.endsAt - Date.now()) / (24 * 60 * 60 * 1000), 1)) * 20; const engagement = Math.min((auction.socialEngagement.likes + auction.socialEngagement.shares) / 10, 20); const viewActivity = Math.min(auction.viewCount / 50, 20); return bidActivity + timeRemaining + engagement + viewActivity; } }