@gotake/gotake-sdk
Version:
SDK for interacting with GoTake blockchain contracts
250 lines (249 loc) • 8.97 kB
JavaScript
import { BigNumber } from 'ethers';
/**
* Utility functions for content management
*/
export class ContentUtils {
/**
* Validate content ID
* @param contentId Content ID to validate
* @returns True if content ID is valid
*/
static isValidContentId(contentId) {
return Number.isInteger(contentId) && contentId > 0;
}
/**
* Format price for display
* @param price Price in wei/smallest unit
* @param decimals Token decimals (default 18 for ETH)
* @param maxDecimals Maximum decimal places to show
* @returns Formatted price string
*/
static formatPrice(price, decimals = 18, maxDecimals = 6) {
const ethers = require('ethers');
const formatted = ethers.utils.formatUnits(price, decimals);
const num = parseFloat(formatted);
if (num === 0)
return '0';
if (num < 0.000001)
return '< 0.000001';
// Round to maxDecimals
return num.toFixed(Math.min(maxDecimals, decimals)).replace(/\.?0+$/, '');
}
/**
* Parse price from string
* @param priceString Price string
* @param decimals Token decimals (default 18 for ETH)
* @returns Price in BigNumber
*/
static parsePrice(priceString, decimals = 18) {
const ethers = require('ethers');
return ethers.utils.parseUnits(priceString.toString(), decimals);
}
/**
* Calculate remaining time in seconds
* @param validUntil Valid until timestamp
* @returns Remaining seconds (0 if expired)
*/
static getRemainingTime(validUntil) {
const now = Math.floor(Date.now() / 1000);
const remaining = validUntil.toNumber() - now;
return Math.max(0, remaining);
}
/**
* Format time duration for display
* @param seconds Duration in seconds
* @returns Formatted duration string
*/
static formatDuration(seconds) {
if (seconds <= 0)
return 'Expired';
const days = Math.floor(seconds / 86400);
const hours = Math.floor((seconds % 86400) / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = seconds % 60;
if (days > 0) {
return `${days}d ${hours}h ${minutes}m`;
}
else if (hours > 0) {
return `${hours}h ${minutes}m`;
}
else if (minutes > 0) {
return `${minutes}m ${remainingSeconds}s`;
}
else {
return `${remainingSeconds}s`;
}
}
/**
* Check if permission is still valid
* @param permission View permission object
* @returns True if permission is valid and not expired
*/
static isPermissionValid(permission) {
if (!permission.isValid)
return false;
if (permission.remainingViews.lte(0))
return false;
// Since validUntil is no longer available, we rely on the isValid flag
return permission.isValid;
}
/**
* Get permission status description
* @param permission View permission object
* @returns Status description string
*/
static getPermissionStatus(permission) {
if (!permission.isValid)
return 'Invalid';
if (permission.remainingViews.lte(0))
return 'No views remaining';
// Since validUntil is no longer available, we consider all valid permissions as not time-expired
const remainingTime = 86400; // Default to 24 hours for display purposes
if (remainingTime <= 0)
return 'Expired';
const views = permission.remainingViews.toNumber();
const timeStr = this.formatDuration(remainingTime);
return `${views} view${views !== 1 ? 's' : ''} remaining, ${timeStr} left`;
}
/**
* Calculate expiry timestamp
* @param durationSeconds Duration in seconds
* @returns Expiry timestamp
*/
static calculateExpiry(durationSeconds) {
const now = Math.floor(Date.now() / 1000);
return BigNumber.from(now + durationSeconds);
}
/**
* Validate content configuration
* @param config Content configuration
* @returns Validation result with errors if any
*/
static validateContentConfig(config) {
const errors = [];
if (!this.isValidContentId(config.contentId)) {
errors.push('Content ID must be a positive integer');
}
if (config.nativePrice.lt(0)) {
errors.push('Native price cannot be negative');
}
if (config.defaultViewCount <= 0) {
errors.push('Default view count must be positive');
}
if (config.viewDuration <= 0) {
errors.push('View duration must be positive');
}
return {
isValid: errors.length === 0,
errors
};
}
/**
* Compare two content configurations
* @param config1 First configuration
* @param config2 Second configuration
* @returns Object showing differences
*/
static compareConfigs(config1, config2) {
const changes = {};
if (!config1.nativePrice.eq(config2.nativePrice)) {
changes.nativePrice = {
old: config1.nativePrice.toString(),
new: config2.nativePrice.toString()
};
}
if (config1.defaultViewCount !== config2.defaultViewCount) {
changes.defaultViewCount = {
old: config1.defaultViewCount,
new: config2.defaultViewCount
};
}
if (config1.viewDuration !== config2.viewDuration) {
changes.viewDuration = {
old: config1.viewDuration,
new: config2.viewDuration
};
}
if (config1.isActive !== config2.isActive) {
changes.isActive = {
old: config1.isActive,
new: config2.isActive
};
}
return {
hasChanges: Object.keys(changes).length > 0,
changes
};
}
/**
* Generate content summary for display
* @param config Content configuration
* @returns Human-readable summary
*/
static generateContentSummary(config) {
const price = this.formatPrice(config.nativePrice);
const duration = this.formatDuration(config.viewDuration);
const status = config.isActive ? 'Active' : 'Inactive';
return `Content ${config.contentId}: ${price} ETH, ${config.defaultViewCount} views, ${duration} validity - ${status}`;
}
/**
* Sort content configurations by various criteria
* @param configs Array of content configurations
* @param sortBy Sort criteria
* @param ascending Sort order
* @returns Sorted configurations
*/
static sortConfigs(configs, sortBy, ascending = true) {
const sorted = [...configs].sort((a, b) => {
let comparison = 0;
switch (sortBy) {
case 'contentId':
comparison = a.contentId - b.contentId;
break;
case 'nativePrice':
comparison = a.nativePrice.gt(b.nativePrice) ? 1 : a.nativePrice.lt(b.nativePrice) ? -1 : 0;
break;
case 'defaultViewCount':
comparison = a.defaultViewCount - b.defaultViewCount;
break;
case 'viewDuration':
comparison = a.viewDuration - b.viewDuration;
break;
case 'isActive':
comparison = Number(b.isActive) - Number(a.isActive); // Active first by default
break;
}
return ascending ? comparison : -comparison;
});
return sorted;
}
/**
* Filter content configurations by criteria
* @param configs Array of content configurations
* @param filters Filter criteria
* @returns Filtered configurations
*/
static filterConfigs(configs, filters) {
return configs.filter(config => {
if (filters.isActive !== undefined && config.isActive !== filters.isActive) {
return false;
}
if (filters.minPrice && config.nativePrice.lt(filters.minPrice)) {
return false;
}
if (filters.maxPrice && config.nativePrice.gt(filters.maxPrice)) {
return false;
}
if (filters.minViewCount && config.defaultViewCount < filters.minViewCount) {
return false;
}
if (filters.maxViewCount && config.defaultViewCount > filters.maxViewCount) {
return false;
}
if (filters.contentIds && !filters.contentIds.includes(config.contentId)) {
return false;
}
return true;
});
}
}