UNPKG

@agentdao/core

Version:

Core functionality, skills, and ready-made UI components for AgentDAO - Web3 subscriptions, content generation, social media, help support, live chat, RSS fetching, web search, and agent pricing integration

426 lines (425 loc) 17 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EnhancedWeb3SubscriptionSkill = void 0; const ethers_1 = require("ethers"); const Web3SubscriptionSkill_1 = require("./Web3SubscriptionSkill"); class EnhancedWeb3SubscriptionSkill extends Web3SubscriptionSkill_1.Web3SubscriptionSkill { constructor(config) { super(config); this.webhookQueue = []; this.analyticsCache = new Map(); // Validate safe address configuration const safeAddress = config.platformFee?.safeAddress || process.env.PLATFORM_SAFE_ADDRESS || ''; if (!safeAddress) { console.warn('⚠️ PLATFORM_SAFE_ADDRESS not configured. Platform fees will not be properly tracked.'); } else if (!safeAddress.startsWith('0x') || safeAddress.length !== 42) { console.warn('⚠️ PLATFORM_SAFE_ADDRESS appears to be invalid. Expected 42-character hex address starting with 0x.'); } this.enhancedConfig = { ...config, platformFee: { percentage: 10, // Reduced from 15% to 10% safeAddress, autoTransfer: true, ...config.platformFee }, chains: { 8453: { name: 'Base', rpcUrl: 'https://mainnet.base.org', explorer: 'https://basescan.org', nativeToken: 'ETH', ...config.chains?.[8453] }, ...config.chains }, tokens: { // Default ADAO token on Base [config.adaoToken.address]: { name: 'ADAO Token', symbol: 'ADAO', decimals: config.adaoToken.decimals, chainId: 8453, ...config.tokens?.[config.adaoToken.address] }, ...config.tokens } }; } // ===================== // BACKWARD COMPATIBLE METHODS (unchanged) // ===================== // All existing methods work exactly the same // createSubscription, checkSubscription, cancelSubscription, etc. // are inherited from Web3SubscriptionSkill // ===================== // NEW ENHANCED METHODS // ===================== /** * Get available payment methods for a user across all chains */ async getPaymentMethods(userAddress) { const methods = []; for (const [tokenAddress, tokenConfig] of Object.entries(this.enhancedConfig.tokens || {})) { try { const provider = new ethers_1.ethers.JsonRpcProvider(this.enhancedConfig.chains?.[tokenConfig.chainId]?.rpcUrl); if (tokenAddress === 'native') { // Native token (ETH, MATIC, etc.) const balance = await provider.getBalance(userAddress); const balanceFormatted = parseFloat(ethers_1.ethers.formatEther(balance)); methods.push({ type: 'native', address: 'native', symbol: tokenConfig.symbol, decimals: 18, chainId: tokenConfig.chainId, balance: balanceFormatted }); } else { // ERC-20 token const tokenContract = new ethers_1.ethers.Contract(tokenAddress, ['function balanceOf(address) view returns (uint256)'], provider); const balance = await tokenContract.balanceOf(userAddress); const balanceFormatted = parseFloat(ethers_1.ethers.formatUnits(balance, tokenConfig.decimals)); methods.push({ type: 'token', address: tokenAddress, symbol: tokenConfig.symbol, decimals: tokenConfig.decimals, chainId: tokenConfig.chainId, balance: balanceFormatted }); } } catch (error) { console.warn(`Failed to get balance for token ${tokenAddress}:`, error); } } return methods; } /** * Create subscription with enhanced features */ async createEnhancedSubscription(userAddress, planId, billingPeriod, paymentMethod) { // Create base subscription const baseSubscription = await this.createSubscription(userAddress, planId, billingPeriod); // Enhance with additional data const enhancedSubscription = { ...baseSubscription, paymentMethod, chainId: paymentMethod.chainId, analytics: { totalPayments: 1, totalRevenue: baseSubscription.price, averageSessionDuration: 0, featureUsage: {}, churnRate: 0, lifetimeValue: baseSubscription.price } }; // Send webhook await this.sendWebhook('subscriptionCreated', { subscriptionId: enhancedSubscription.id, userAddress, planId, billingPeriod, paymentMethod, amount: baseSubscription.price }); // Track analytics await this.trackAnalytics('subscription_created', { userAddress, planId, amount: baseSubscription.price, paymentMethod: paymentMethod.symbol }); return enhancedSubscription; } /** * Upgrade subscription to a higher plan */ async upgradeSubscription(userAddress, newPlanId, newPeriod) { const currentStatus = await this.checkSubscription(userAddress); if (!currentStatus.hasActiveSubscription) { throw new Error('No active subscription found'); } const currentPlan = this.enhancedConfig.plans[currentStatus.planId]; const newPlan = this.enhancedConfig.plans[newPlanId]; if (!newPlan) { throw new Error(`Plan ${newPlanId} not found`); } // Calculate prorated amount const proratedAmount = await this.calculateProratedUpgrade(userAddress, currentStatus.planId, newPlanId, newPeriod || currentStatus.billingPeriod); // Create upgrade record const upgradeRecord = { id: `upgrade-${Date.now()}`, fromPlan: currentStatus.planId, toPlan: newPlanId, fromPeriod: currentStatus.billingPeriod, toPeriod: newPeriod || currentStatus.billingPeriod, proratedAmount, timestamp: new Date() }; // Process the upgrade (this would interact with smart contracts) console.log(`Upgrading subscription for user: ${userAddress}`); console.log(`From: ${currentStatus.planId} to: ${newPlanId}`); console.log(`Prorated amount: ${proratedAmount}`); // Send webhook await this.sendWebhook('subscriptionUpgraded', { userAddress, fromPlan: currentStatus.planId, toPlan: newPlanId, proratedAmount }); // Return enhanced subscription return { id: `upgraded-${Date.now()}`, userAddress, planId: newPlanId, planName: newPlan.name, billingPeriod: newPeriod || currentStatus.billingPeriod, price: proratedAmount, monthlyPrice: this.calculateEnhancedMonthlyPrice(proratedAmount, newPeriod || currentStatus.billingPeriod), startDate: new Date(), endDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days nextBillingDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), status: 'active', features: newPlan.features, discountApplied: 0, paymentMethod: await this.getDefaultPaymentMethod(userAddress), chainId: 8453, // Default to Base upgradeHistory: [upgradeRecord] }; } /** * Downgrade subscription to a lower plan */ async downgradeSubscription(userAddress, newPlanId, newPeriod) { const currentStatus = await this.checkSubscription(userAddress); if (!currentStatus.hasActiveSubscription) { throw new Error('No active subscription found'); } const newPlan = this.enhancedConfig.plans[newPlanId]; if (!newPlan) { throw new Error(`Plan ${newPlanId} not found`); } // Calculate credit for downgrade const creditAmount = await this.calculateDowngradeCredit(userAddress, currentStatus.planId, newPlanId, newPeriod || currentStatus.billingPeriod); // Process the downgrade console.log(`Downgrading subscription for user: ${userAddress}`); console.log(`To: ${newPlanId}, Credit: ${creditAmount}`); // Send webhook await this.sendWebhook('subscriptionDowngraded', { userAddress, toPlan: newPlanId, creditAmount }); return { id: `downgraded-${Date.now()}`, userAddress, planId: newPlanId, planName: newPlan.name, billingPeriod: newPeriod || currentStatus.billingPeriod, price: 0, // No charge for downgrade monthlyPrice: 0, startDate: new Date(), endDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), nextBillingDate: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), status: 'active', features: newPlan.features, discountApplied: 0, paymentMethod: await this.getDefaultPaymentMethod(userAddress), chainId: 8453 }; } /** * Process refund for a subscription */ async processRefund(userAddress, amount, reason) { if (!this.enhancedConfig.refunds?.enabled) { throw new Error('Refunds are not enabled for this subscription'); } const currentStatus = await this.checkSubscription(userAddress); if (!currentStatus.hasActiveSubscription) { throw new Error('No active subscription found'); } const refundRecord = { id: `refund-${Date.now()}`, amount, reason, status: this.enhancedConfig.refunds.requireApproval ? 'pending' : 'approved', timestamp: new Date() }; // If auto-refund is enabled, process immediately if (this.enhancedConfig.refunds.autoRefund && !this.enhancedConfig.refunds.requireApproval) { refundRecord.status = 'processed'; // Here you would interact with smart contracts to process the refund console.log(`Processing refund of ${amount} for user: ${userAddress}`); } // Send webhook await this.sendWebhook('refundIssued', { userAddress, amount, reason, status: refundRecord.status }); return refundRecord; } /** * Get enhanced analytics for a subscription */ async getEnhancedAnalytics(userAddress) { const cached = this.analyticsCache.get(userAddress); if (cached) { return cached; } const subscription = await this.checkSubscription(userAddress); if (!subscription.hasActiveSubscription) { throw new Error('No active subscription found'); } // Mock analytics data (in real implementation, this would come from database) const analytics = { totalPayments: 1, totalRevenue: 100, // Mock data averageSessionDuration: 45, // minutes featureUsage: { chat: 150, analytics: 25, support: 10 }, churnRate: 0.05, // 5% lifetimeValue: 100 }; this.analyticsCache.set(userAddress, analytics); return analytics; } /** * Get enhanced revenue statistics */ async getEnhancedRevenueStats() { const baseStats = await this.getRevenueStats(); // Mock enhanced data (in real implementation, this would come from database) const enhancedStats = { ...baseStats, byChain: { 8453: baseStats.totalRevenue * 0.8, // 80% on Base 137: baseStats.totalRevenue * 0.2 // 20% on Polygon }, byToken: { [this.enhancedConfig.adaoToken.address]: baseStats.totalRevenue * 0.7, // 70% ADAO '0x0000000000000000000000000000000000000000': baseStats.totalRevenue * 0.3 // 30% native }, byPeriod: { monthly: baseStats.totalRevenue * 0.4, quarterly: baseStats.totalRevenue * 0.35, annually: baseStats.totalRevenue * 0.25 }, churnRate: 0.05, averageLifetimeValue: 250, refundRate: 0.02, upgradeRate: 0.15 }; return enhancedStats; } // ===================== // PRIVATE HELPER METHODS // ===================== async sendWebhook(event, data) { const webhookUrl = this.enhancedConfig.webhooks?.[event]; if (!webhookUrl) return; const payload = { event, subscriptionId: data.subscriptionId || 'unknown', userAddress: data.userAddress, data, timestamp: new Date() }; try { const response = await fetch(webhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json', 'User-Agent': 'AgentDAO-Web3Subscription/1.0' }, body: JSON.stringify(payload) }); if (!response.ok) { console.warn(`Webhook failed for event ${event}:`, response.statusText); // Queue for retry this.webhookQueue.push(payload); } } catch (error) { console.error(`Webhook error for event ${event}:`, error); this.webhookQueue.push(payload); } } async trackAnalytics(event, data) { if (!this.enhancedConfig.enhancedAnalytics?.trackUsage) return; try { // Send to analytics endpoint await fetch('/api/analytics/track', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ event, agentId: this.enhancedConfig.agentId, data, timestamp: new Date().toISOString() }) }); } catch (error) { console.error('Analytics tracking failed:', error); } } async getDefaultPaymentMethod(userAddress) { const methods = await this.getPaymentMethods(userAddress); return methods[0] || { type: 'token', address: this.enhancedConfig.adaoToken.address, symbol: 'ADAO', decimals: this.enhancedConfig.adaoToken.decimals, chainId: 8453, balance: 0 }; } async calculateProratedUpgrade(userAddress, fromPlanId, toPlanId, newPeriod) { const fromPlan = this.enhancedConfig.plans[fromPlanId]; const toPlan = this.enhancedConfig.plans[toPlanId]; if (!fromPlan || !toPlan) { throw new Error('Invalid plan configuration'); } const fromPrice = fromPlan.pricing[newPeriod].price; const toPrice = toPlan.pricing[newPeriod].price; // Simple proration: difference between plans return Math.max(0, toPrice - fromPrice); } async calculateDowngradeCredit(userAddress, fromPlanId, toPlanId, newPeriod) { const fromPlan = this.enhancedConfig.plans[fromPlanId]; const toPlan = this.enhancedConfig.plans[toPlanId]; if (!fromPlan || !toPlan) { throw new Error('Invalid plan configuration'); } const fromPrice = fromPlan.pricing[newPeriod].price; const toPrice = toPlan.pricing[newPeriod].price; // Credit for downgrade return Math.max(0, fromPrice - toPrice); } calculateEnhancedMonthlyPrice(price, billingPeriod) { switch (billingPeriod) { case 'monthly': return price; case 'quarterly': return price / 3; case 'annually': return price / 12; default: return price; } } } exports.EnhancedWeb3SubscriptionSkill = EnhancedWeb3SubscriptionSkill;