@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
415 lines (414 loc) • 16.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Web3SubscriptionSkill = void 0;
const ethers_1 = require("ethers");
class Web3SubscriptionSkill {
constructor(config) {
this.config = config;
this.provider = new ethers_1.ethers.JsonRpcProvider(config.provider.rpcUrl);
}
/**
* Set the signer for transactions
*/
setSigner(signer) {
this.signer = signer;
}
/**
* Create a new subscription
*/
async createSubscription(userAddress, planId, billingPeriod) {
const plan = this.config.plans[planId];
if (!plan) {
throw new Error(`Plan ${planId} not found`);
}
const pricing = plan.pricing[billingPeriod];
const price = pricing.price;
const discount = pricing.discount;
// Calculate dates
const startDate = new Date();
const endDate = new Date(startDate);
switch (billingPeriod) {
case 'monthly':
endDate.setMonth(endDate.getMonth() + 1);
break;
case 'quarterly':
endDate.setMonth(endDate.getMonth() + 3);
break;
case 'annually':
endDate.setFullYear(endDate.getFullYear() + 1);
break;
}
// Check if user has enough ADAO tokens
const balance = await this.getADaoBalance(userAddress);
if (balance < price) {
throw new Error(`Insufficient ADAO balance. Required: ${price}, Available: ${balance}`);
}
// Approve tokens if needed
await this.approveADao(userAddress, price);
// Create subscription (this would interact with smart contract)
const subscriptionId = this.generateSubscriptionId(userAddress, planId);
const subscription = {
id: subscriptionId,
userAddress,
planId,
planName: plan.name,
billingPeriod,
price,
monthlyPrice: this.calculateMonthlyPrice(price, billingPeriod),
startDate,
endDate,
nextBillingDate: endDate,
status: 'active',
features: plan.features,
discountApplied: discount
};
// Track monetization - call our platform API
try {
const agentId = this.config.agentId;
const response = await fetch('/api/skills/web3-subscription/process-payment', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
agent_id: agentId,
user_address: userAddress,
subscription_id: subscriptionId,
plan_id: planId,
plan_name: plan.name,
billing_period: billingPeriod,
amount: price,
payment_token: 'ADAO',
transaction_hash: null // Would be set after smart contract interaction
})
});
if (response.ok) {
const result = await response.json();
console.log('Monetization tracked:', result);
}
else {
console.warn('Failed to track monetization:', await response.text());
}
}
catch (error) {
console.error('Error tracking monetization:', error);
// Don't fail the subscription creation if monetization tracking fails
}
// Here you would typically:
// 1. Call smart contract to transfer tokens
// 2. Store subscription in database
// 3. Send webhook notification
// 4. Update analytics
return subscription;
}
/**
* Check subscription status for a user
*/
async checkSubscription(userAddress) {
if (!this.config.database || !this.config.database.endpoint) {
throw new Error('No database endpoint configured for subscription management. Please configure a database endpoint to use this feature.');
}
try {
const response = await fetch(`${this.config.database.endpoint}?type=subscription&userAddress=${userAddress}`, {
headers: {
...(this.config.database.apiKey ? { 'Authorization': `Bearer ${this.config.database.apiKey}` } : {})
}
});
if (!response.ok) {
throw new Error(`Failed to fetch subscription: ${response.statusText}`);
}
const data = await response.json();
if (!data.subscription) {
return {
hasActiveSubscription: false
};
}
const subscription = data.subscription;
const now = new Date();
const daysLeft = Math.ceil((subscription.endDate.getTime() - now.getTime()) / (1000 * 60 * 60 * 24));
return {
hasActiveSubscription: daysLeft > 0,
planId: subscription.planId,
planName: subscription.planName,
billingPeriod: subscription.billingPeriod,
daysLeft: Math.max(0, daysLeft),
nextBillingDate: subscription.nextBillingDate,
features: subscription.features,
isTrial: subscription.status === 'trial',
trialDaysLeft: subscription.status === 'trial' ? daysLeft : undefined
};
}
catch (error) {
throw new Error(`Failed to check subscription: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Cancel a subscription
*/
async cancelSubscription(userAddress) {
if (!this.config.database || !this.config.database.endpoint) {
throw new Error('No database endpoint configured for subscription management. Please configure a database endpoint to use this feature.');
}
try {
const response = await fetch(this.config.database.endpoint, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
...(this.config.database.apiKey ? { 'Authorization': `Bearer ${this.config.database.apiKey}` } : {})
},
body: JSON.stringify({
type: 'cancel_subscription',
userAddress,
agentId: this.config.agentId,
agentName: this.config.agentName,
domain: this.config.domain,
timestamp: new Date().toISOString()
})
});
return response.ok;
}
catch (error) {
throw new Error(`Failed to cancel subscription: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Get subscription history for a user
*/
async getSubscriptionHistory(userAddress) {
if (!this.config.database || !this.config.database.endpoint) {
throw new Error('No database endpoint configured for subscription management. Please configure a database endpoint to use this feature.');
}
try {
const response = await fetch(`${this.config.database.endpoint}?type=subscription_history&userAddress=${userAddress}`, {
headers: {
...(this.config.database.apiKey ? { 'Authorization': `Bearer ${this.config.database.apiKey}` } : {})
}
});
if (!response.ok) {
throw new Error(`Failed to fetch subscription history: ${response.statusText}`);
}
const data = await response.json();
return data.subscriptions || [];
}
catch (error) {
throw new Error(`Failed to get subscription history: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Change billing period for existing subscription
*/
async changeBillingPeriod(userAddress, newPeriod) {
const currentStatus = await this.checkSubscription(userAddress);
if (!currentStatus.hasActiveSubscription) {
throw new Error('No active subscription found');
}
// Calculate prorated amount
const proratedAmount = await this.getProratedAmount(userAddress, currentStatus.planId, newPeriod);
// Process the change
console.log(`Changing billing period to ${newPeriod} for user: ${userAddress}`);
return true;
}
/**
* Get prorated amount for plan/billing period change
*/
async getProratedAmount(userAddress, newPlanId, newPeriod) {
const plan = this.config.plans[newPlanId];
if (!plan) {
throw new Error(`Plan ${newPlanId} not found`);
}
const pricing = plan.pricing[newPeriod];
return pricing.price;
}
/**
* Get plan pricing information
*/
async getPlanPricing(planId) {
const plan = this.config.plans[planId];
if (!plan) {
throw new Error(`Plan ${planId} not found`);
}
return {
planId,
planName: plan.name,
monthly: plan.pricing.monthly,
quarterly: plan.pricing.quarterly,
annually: plan.pricing.annually,
features: plan.features
};
}
/**
* Get all available plans
*/
async getAvailablePlans() {
return Object.entries(this.config.plans).map(([id, plan]) => ({
id,
name: plan.name,
description: plan.description,
pricing: {
planId: id,
planName: plan.name,
monthly: plan.pricing.monthly,
quarterly: plan.pricing.quarterly,
annually: plan.pricing.annually,
features: plan.features
},
features: plan.features,
maxUsers: plan.maxUsers
}));
}
/**
* Calculate savings percentage for a billing period
*/
async calculateSavings(planId, period) {
const plan = this.config.plans[planId];
if (!plan) {
throw new Error(`Plan ${planId} not found`);
}
const pricing = plan.pricing[period];
return pricing.discount;
}
/**
* Get ADAO token balance for a user
*/
async getADaoBalance(userAddress) {
try {
const tokenContract = new ethers_1.ethers.Contract(this.config.adaoToken.address, ['function balanceOf(address) view returns (uint256)'], this.provider);
const balance = await tokenContract.balanceOf(userAddress);
return parseFloat(ethers_1.ethers.formatUnits(balance, this.config.adaoToken.decimals));
}
catch (error) {
console.error('Error getting ADAO balance:', error);
return 0;
}
}
/**
* Approve ADAO tokens for spending
*/
async approveADao(userAddress, amount) {
if (!this.signer) {
throw new Error('Signer not set. Call setSigner() first.');
}
try {
const tokenContract = new ethers_1.ethers.Contract(this.config.adaoToken.address, [
'function approve(address spender, uint256 amount) returns (bool)',
'function allowance(address owner, address spender) view returns (uint256)'
], this.signer);
const amountWei = ethers_1.ethers.parseUnits(amount.toString(), this.config.adaoToken.decimals);
// Check current allowance
const currentAllowance = await tokenContract.allowance(userAddress, await this.signer.getAddress());
if (currentAllowance < amountWei) {
const tx = await tokenContract.approve(await this.signer.getAddress(), amountWei);
await tx.wait();
}
return true;
}
catch (error) {
console.error('Error approving ADAO tokens:', error);
return false;
}
}
/**
* Update plan configuration (admin only)
*/
async updatePlan(planId, plan) {
if (!this.config.database || !this.config.database.endpoint) {
throw new Error('No database endpoint configured for plan management. Please configure a database endpoint to use this feature.');
}
try {
const response = await fetch(this.config.database.endpoint, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
...(this.config.database.apiKey ? { 'Authorization': `Bearer ${this.config.database.apiKey}` } : {})
},
body: JSON.stringify({
type: 'update_plan',
planId,
plan,
agentId: this.config.agentId,
agentName: this.config.agentName,
domain: this.config.domain,
timestamp: new Date().toISOString()
})
});
return response.ok;
}
catch (error) {
throw new Error(`Failed to update plan: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Get revenue statistics (admin only)
*/
async getRevenueStats() {
if (!this.config.database || !this.config.database.endpoint) {
throw new Error('No database endpoint configured for revenue analytics. Please configure a database endpoint to use this feature.');
}
try {
const response = await fetch(`${this.config.database.endpoint}?type=revenue_stats`, {
headers: {
...(this.config.database.apiKey ? { 'Authorization': `Bearer ${this.config.database.apiKey}` } : {})
}
});
if (!response.ok) {
throw new Error(`Failed to fetch revenue stats: ${response.statusText}`);
}
const data = await response.json();
return data;
}
catch (error) {
throw new Error(`Failed to get revenue stats: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Get ADAO token price in USD
*/
async getADaoPrice() {
if (!this.config.database || !this.config.database.endpoint) {
throw new Error('No database endpoint configured for price data. Please configure a database endpoint to use this feature.');
}
try {
const response = await fetch(`${this.config.database.endpoint}?type=adao_price`, {
headers: {
...(this.config.database.apiKey ? { 'Authorization': `Bearer ${this.config.database.apiKey}` } : {})
}
});
if (!response.ok) {
throw new Error(`Failed to fetch ADAO price: ${response.statusText}`);
}
const data = await response.json();
return data.price;
}
catch (error) {
throw new Error(`Failed to get ADAO price: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Estimate gas for subscription transaction
*/
async estimateGas(userAddress, planId, period) {
const plan = this.config.plans[planId];
if (!plan) {
throw new Error(`Plan ${planId} not found`);
}
const price = plan.pricing[period].price;
// This would typically estimate gas for the actual smart contract call
// For now, returning a rough estimate
return 150000; // 150k gas
}
// Private helper methods
generateSubscriptionId(userAddress, planId) {
return `${userAddress}-${planId}-${Date.now()}`;
}
calculateMonthlyPrice(price, billingPeriod) {
switch (billingPeriod) {
case 'monthly':
return price;
case 'quarterly':
return price / 3;
case 'annually':
return price / 12;
default:
return price;
}
}
}
exports.Web3SubscriptionSkill = Web3SubscriptionSkill;